Pap.er 模仿 - 第一天

最后更新: 2017-12-15

一、 项目初始化

  1. 解析对应的资源, 下载Pap.er之后,需要解析里面的资源.
    采用如下的方法: http://blog.csdn.net/xuzihai0703/article/details/50327531

  2. 创建项目工程
    项目结构

  3. 导入里面的资源icon, 然后运行
    可以看到,资源文件夹里面有一个AppIcon.icns 文件夹, 里面包含搜有的icon资源.可以在 这里 进行解析、下载, 添加到项目Assets.xcassets - AppIcon中, cmd + R运行;
    运行结果
    运行之后,什么都没有,当然是这样的,毕竟我们什么都没做

  4. 在状态栏显示App, 进行如下操作

    • 把资源文件夹内的两张 icon16*16的文件,放到 Assets里面去

    关于StatusBar icon尺寸可以参考这篇文章

    • applicationDidFinishLaunching 的上面通过 NSStatusBar 实例化一个 NSStatusItem 对象, 通过设置此对象, 我们可以进行一系列的操作,例如icon、title、action等.

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {

    @IBOutlet weak var window: NSWindow!
    
    let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength)

    func applicationDidFinishLaunching(_ aNotification: Notification) {
        // Insert code here to initialize your application
        let icon = NSImage(named: NSImage.Name(rawValue: "icon16*16"))
        icon?.isTemplate = true
        statusItem.image = icon
        statusItem.button?.target = self
        statusItem.button?.action = #selector(onStatusItemDidClicked(_:))
    }

    @objc private func onStatusItemDidClicked(_ item: NSStatusItem) {
        print("onStatusItemDidClicked")
    }

}
```

  • cmd + R 运行一下, 跑起来了。 状态栏出现了statusBarIcon, 点击一下, 控制台出现打印信息。 Good!

    动手一下: 删除 icon?.isTemplate = true 会是什么效果?

二、处理 Dock栏以 及 状态栏左

当我们点击cmd + R 运行时候, 会出现如下情况:

  • 左边的状态栏 StatusLeft

  • Dock栏 Dock 以及显示一个系统默认给加载的窗口.

找到 MainMenu.xib, 然后删除 Main Menu 以及 Window, 然后运行。

删除Menu以及window

运行好之后,你会发现,状态栏左在没有那多选项了,但是还是存在一个 . Dock 栏以后存在, 值得称赞的是,系统默认加载的 Window 消失了

这是你需要在系统设置文件 info.plist 设置 Application is agent (UIElement) = YES 即可, 如下图

Application is agent (UIElement)

对了,别忘记删除 applicationDidFinishLaunching 上面的 @IBOutlet weak var window: NSWindow!, 因为window 以及被我们干没了。

三、NSPopover 使用

观察官方的 Pap.er, 点击 状态栏icon, 会出现如下界面:

.

macOS开发中,系统为我们提供了 NSPopover 来实现此效果。

3.1 NSPopover 属性介绍

查看NSPopover文档, 简单介绍其属性。

属性 说明
@IBOutlet weak open var delegate: NSPopoverDelegate? 代理
open var appearance: NSAppearance? popover的外观, 默认 NSAppearanceNameVibrantLight
open var behavior: NSPopover.Behavior Popover 的行为:
applicationDefined 表示NSPopover的关闭需要App自己负责控制;
transient: 表示只要点击到NSPopover显示的窗口之外就自动关闭;
semitransient: 表示 只要点击到NSPopover显示的窗口之外就自动关闭,但是点击到当前App 窗口之外不会关闭。
open var animates: Bool 是否动画
@IBOutlet open var contentViewController: NSViewController? popover 内容展示的控制器, 在show之前一定要设置
open var contentSize: NSSize popover 显示的大小
方法
open func show(relativeTo positioningRect: NSRect, of positioningView: NSView, preferredEdge: NSRectEdge) 显示
@IBAction open func performClose(_ sender: Any?) 关闭popover 但是有可能会关不掉
open func close() 强制关闭

3.2 JUST DO IT

简单介绍之后,我们开始动手吧。
Step1:applicationDidFinishLaunching 上面添加一个 NSPopover 的实例;

let popover = NSPopover()

Step2:applicationDidFinishLaunching 里面添加如下设置:

popover.behavior = .transient
popover.animates = true
popover.contentSize = CGSize(width: 285, height: 600)

Step3:statusItem.button的响应方法中添加对应的响应

if !self.popover.isShown {
    self.popover.show(relativeTo: statusBarButton.bounds, of: statusBarButton, preferredEdge: .minY)
}

仅仅这几步还是不行的, 我们忘记了最重要的设置 contentViewController.点击 File->New->File->MacOS->Cocoa Class, 新建一个 MainPopoverViewController, 我们采用xib 来布局视图,当然 这不是必须的;

Last Step: 在 Step2 设置 popover 的地方,添加一句

popover.contentViewController =  MainPopoverViewController(nibName: NSNib.Name(rawValue: "MainPopoverViewController"), bundle: nil)

设置完之后, cmd + R 运行, 点击Status Item。显示如下,Great!

3.3 细节优化

  1. Popover 展示出来之后, 用户如果点击非App 区域, 那么需要Popover 消失, 有如下两种方式:
  • 获取用户点击的区域, 然后判断用户点击是否在Popover的 contentViewController上-- 此种方式比较复杂
  • 根据 NSApplicationDelegateapplicationWillResignActive 通知处理,简单,有效。
    func applicationWillResignActive(_ notification: Notification) {
        self.popover.close()
    }
    
  1. MainPopoverViewController 从nib中加载出来,但是外界其实不要知道其实例化方式, 给 MainPopoverViewController 提供一个类方法实例化即可。
extension MainPopoverViewController {
    static func make() -> MainPopoverViewController {
        let vc = MainPopoverViewController(nibName: NSNib.Name(rawValue: "MainPopoverViewController"), bundle: nil)
        return vc
    }
}

四、代码

AppDelegate.swift

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
    
    let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength)
    
    let popover = NSPopover()
    
    func applicationDidFinishLaunching(_ aNotification: Notification) {
        // Insert code here to initialize your application
        let icon = NSImage(named: NSImage.Name(rawValue: "icon16*16"))
        icon?.isTemplate = true
        statusItem.image = icon
        statusItem.button?.target = self
        statusItem.button?.action = #selector(onStatusItemDidClicked(_:))
        
        popover.contentViewController =  MainPopoverViewController.make()
        popover.behavior = .transient
        popover.animates = true
        popover.contentSize = CGSize(width: 285, height: 600)
        
    }
    
    @objc private func onStatusItemDidClicked(_ statusBarButton: NSStatusBarButton) {
        print("onStatusItemDidClicked")
        if !self.popover.isShown {
            self.popover.show(relativeTo: statusBarButton.bounds, of: statusBarButton, preferredEdge: .minY)
        }
    }
    
    func applicationWillResignActive(_ notification: Notification) {
        self.popover.close()
    }
}

MainPopoverViewController.swift

extension MainPopoverViewController {
    static func make() -> MainPopoverViewController {
        let vc = MainPopoverViewController(nibName: NSNib.Name(rawValue: "MainPopoverViewController"), bundle: nil)
        return vc
    }
}

class MainPopoverViewController: NSViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
}

posted @ 2017-12-15 21:36  洒水先生  阅读(334)  评论(0编辑  收藏  举报