Environments / 环境要求
- macOS Sierra 10.12.1 (16B2657)
- Xcode 8.1 (8B62)
可以通过 https://developer.apple.com/ 获取更多配置环境信息。
Get Started / 上手实践
要实现 NSTouchBar,我们必须做下面三件事情:
- 继承 NSResponder
- 遵循 NSTouchBarProvider 协议
- 在 NSTouchBarProvider 协议中实现
makeTouchBar()
方法
我们都知道,NSViewController 是 NSResponder 的一个子类实现(如果对 NSResponder 的原理不了解,可以参看这篇文章)。在 NSTouchBarProvider 的定义下方,可以看到在 10.12.1 中的 NSResponder 已经遵循了 NSTouchBarProvider 协议:
public protocol NSTouchBarProvider : NSObjectProtocol {
@available(OSX 10.12.1, *)
public var touchBar: NSTouchBar? { get }
}
extension NSResponder : NSTouchBarProvider {
@available(OSX 10.12.1, *)
open var touchBar: NSTouchBar?
@available(OSX 10.12.1, *)
open func makeTouchBar() -> NSTouchBar?
}
所以,对于开发者来说,你要做的,就是对任何一个继承自 NSResponder 的元素,去实现 makeTouchBar()
方法。
我们来做一个最简单的 TouchBar 应用:在屏幕上随机生成两个 0 到 9 的整数,通过点击 TouchBar 中的按钮来比较两个数大小,并给出结果。完成后的效果如图:
Project Init / 新建工程
打开 Xcode,并新建一个 Xcode 工程。选择 macOS 上的 Cocoa Application,点击下一步,输入项目名称 TouchBarSample,并在勾选使用 Storyboards。在这里我们选择使用 Swift 3.0 开发。
进入 Main.storyboard,删除默认的 Window Controller 和 View Controller。我们在左边的项目中新建我们的 WindowController。选择新建一个 Cocoa Class,类名为 WindowController,继承自 NSWindowController 并勾选使用 XIB 文件。
打开 WindowController.swift
,重写其 windowNibName
属性:
override var windowNibName: String? {
return "WindowController"
}
接下来,修改在 AppDelegate.swift
中的代码,让刚刚创建的 WindowController 显示在屏幕上:
import Cocoa
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
var windowController: NSWindowController?
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Insert code here to initialize your application
let windowController = WindowController()
windowController.showWindow(self)
self.windowController = windowController
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
}
再点击 WindowController.xib
,拖拽组件,摆放如下图所示(一共五个 NSTextField,以及一个 NSButton):
现在,可以转战 WindowController.swift
,专注于 TouchBar 的开发了。
Implement TouchBar / 实现 TouchBar
在实现之前,先来结合文档了解一下接下来所使用到的属性和方法。
open class NSTouchBar : NSObject, NSCoding {
// 初始化方法
public init()
public init?(coder aDecoder: NSCoder)
// 用于标识自定义 TouchBar
// 如果标识符不存在,则无法自定义 TouchBar 中的内容
open var customizationIdentifier: NSTouchBarCustomizationIdentifier?
// 存储 TouchBar 中不同 Item 的标识的数组
// 分别对应了
// 1. 可自定义的 BarItem 对象
// 2. 不可移除的 BarItem 对象
// 3. 默认显示的 BarItem 对象
open var customizationAllowedItemIdentifiers: [NSTouchBarItemIdentifier]
open var customizationRequiredItemIdentifiers: [NSTouchBarItemIdentifier]
open var defaultItemIdentifiers: [NSTouchBarItemIdentifier]
// 用于动态生成 BarItem
// 需要实现 NSTouchBarDelegate 协议
weak open var delegate: NSTouchBarDelegate?
}
其他的属性和方法在这里不再深究。通过初步了解,为了实现一个能够自定义的、最基本的 TouchBar,你需要:
- 给你的 TouchBar 和 Bar Item 生成标识符
- 告诉 TouchBar 默认会显示哪些 Bar Item
- 让 TouchBar 生成这些 Bar Item
给你的 TouchBar 和 Bar Item 生成标识符
打卡 WindowController.swift
,在 import Cocoa
和 class WindowController
中间插入以下代码,用于生成 TouchBar 和 Bar Item 的标识符:
fileprivate extension NSTouchBarCustomizationIdentifier {
static let touchBar = NSTouchBarCustomizationIdentifier("io.Cee.TouchBarSample.touchBar")
}
fileprivate extension NSTouchBarItemIdentifier {
static let smaller = NSTouchBarItemIdentifier("io.Cee.TouchBarSample.smaller")
static let equal = NSTouchBarItemIdentifier("io.Cee.TouchBarSample.equal")
static let bigger = NSTouchBarItemIdentifier("io.Cee.TouchBarSample.bigger")
}
告诉 TouchBar 默认会显示哪些 Bar Item
有了这些标识符,你就可以自定义 TouchBar 中间显示的内容了。在 windowDidLoad()
方法下方实现 makeTouchBar()
:
// MARK: - NSTouchBar
@available(OSX 10.12.1, *)
override func makeTouchBar() -> NSTouchBar? {
let touchBar = NSTouchBar()
// 接下来让 WindowController 实现 NSTouchBarDelegate
// 生成 < = > 三个比较符号
touchBar.delegate = self
// 给 TouchBar 一个标识
touchBar.customizationIdentifier = .touchBar
// 提供默认的 Bar Item 选项
touchBar.defaultItemIdentifiers = [.smaller, .equal, .bigger]
touchBar.customizationAllowedItemIdentifiers = [.smaller, .equal, .bigger]
return touchBar
}
让 TouchBar 生成这些 Bar Item
下面来实现 NSTouchBarDelegate。通过不同的标识(identifier)可以区别不同的 Bar Item。
extension WindowController: NSTouchBarDelegate {
@available(OSX 10.12.1, *)
func touchBar(_ touchBar: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItemIdentifier) -> NSTouchBarItem? {
// 生成自定义的 Bar Item
let touchBarItem = NSCustomTouchBarItem(identifier: identifier)
// 根据不同标识生成不同的 title
var title: String
switch identifier {
case NSTouchBarItemIdentifier.smaller: title = "<"
case NSTouchBarItemIdentifier.equal: title = "="
case NSTouchBarItemIdentifier.bigger: title = ">"
default: title = ""
}
let touchBarButton = NSButton(title: title, target: self, action: #selector(compare(with:)))
touchBarItem.view = touchBarButton
return touchBarItem;
}
}
我们在这里使用了自定义的 NSCustomTouchBarItem。系统同时也提供下面几种不同的 BarItem:
- NSGroupTouchBarItem
- NSPopoverTouchBarItem
- NSSliderTouchBarItem
- NSColorPickerTouchBarItem
- NSCandidateListTouchBarItem
完成剩余部分
- 关联组件:使用 Ctrl-drag 将界面组件拖到代码中。
@IBOutlet weak var numberA: NSTextField!
@IBOutlet weak var numberB: NSTextField!
@IBOutlet weak var resultLabel: NSTextField!
- 实现比较方法
compare(with:)
:
// MARK: - Private Method
func compare(with symbol: NSButton) {
let number1 = numberA.intValue
let number2 = numberB.intValue
var result: Bool
switch symbol.title {
case "<": result = (number1 < number2)
case "=": result = (number1 == number2)
case ">": result = (number1 > number2)
default: result = false
}
resultLabel.stringValue = (result == true) ? "Correct" : "Wrong"
}
- 关联 Randomize 按钮,生成随机数:
// MARK: - Button Action
@IBAction func randomize(_ sender: NSButton) {
reset()
}
// MARK: - Private Method
func reset() {
numberA.intValue = randomAInt()
numberB.intValue = randomAInt()
resultLabel.stringValue = ""
}
func randomAInt() -> Int32 {
return Int32(arc4random_uniform(10))
}
- 最后别忘了在
windowDidLoad()
中随机生成两个数。修改windowDidLoad()
方法:
override func windowDidLoad() {
super.windowDidLoad()
reset()
}
大功告成!运行一下你的程序,使用 Command+Shift+5 快速调出模拟的 TouchBar 窗口测试吧!
References - 参考
- 官方的 NSTouchBar 开发文档:https://developer.apple.com/reference/appkit/nstouchbar
- 代码地址:https://github.com/Cee/TouchBarSample