Pap.er 模仿 - 第一天
最后更新: 2017-12-15
一、 项目初始化
-
解析对应的资源, 下载Pap.er之后,需要解析里面的资源.
采用如下的方法: http://blog.csdn.net/xuzihai0703/article/details/50327531 -
创建项目工程
-
导入里面的资源icon, 然后运行
可以看到,资源文件夹里面有一个AppIcon.icns 文件夹, 里面包含搜有的icon资源.可以在 这里 进行解析、下载, 添加到项目Assets.xcassets - AppIcon
中,cmd + R
运行;
运行之后,什么都没有,当然是这样的,毕竟我们什么都没做 -
在状态栏显示App, 进行如下操作
- 把资源文件夹内的两张
icon16*16
的文件,放到Assets
里面去
- 在
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
运行一下, 跑起来了。 状态栏出现了, 点击一下, 控制台出现打印信息。 Good!动手一下: 删除
icon?.isTemplate = true
会是什么效果?
二、处理 Dock栏以 及 状态栏左
当我们点击cmd + R
运行时候, 会出现如下情况:
-
左边的状态栏
-
Dock栏 以及显示一个系统默认给加载的窗口.
找到 MainMenu.xib
, 然后删除 Main Menu
以及 Window
, 然后运行。
运行好之后,你会发现,状态栏左在没有那多选项了,但是还是存在一个 . Dock 栏以后存在, 值得称赞的是,系统默认加载的 Window 消失了
这是你需要在系统设置文件 info.plist
设置 Application is agent (UIElement) = YES
即可, 如下图
对了,别忘记删除 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 细节优化
- Popover 展示出来之后, 用户如果点击非App 区域, 那么需要Popover 消失, 有如下两种方式:
- 获取用户点击的区域, 然后判断用户点击是否在Popover的 contentViewController上-- 此种方式比较复杂
- 根据
NSApplicationDelegate
的applicationWillResignActive
通知处理,简单,有效。func applicationWillResignActive(_ notification: Notification) { self.popover.close() }
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()
}
}