[译] 如何在 macOS 上使用 NSTouchBar

本文翻译自 raywenderlich.comHow to Use NSTouchBar on macOS,已咨询对方网站,可至多翻译 10 篇文章。
各位若有英语阅读能力的话,还是先打赏然后去阅读英文原吧😉。
综上,此翻译版本仅供参考,谢绝转载

也欢迎你点击我的头像查看我翻译的其他 macOS 开发教程💋

对了我跟着这个教程敲代码的时候发现文中所有的 @available(OSX 10.12.1, *) 其实应为 @available(OSX 10.12.2, *),但是出于对原文的尊重没有修改,请各位注意~

等了好久好久终于等到今天之后,Apple 终于发布了新款的 MacBook Pro,它最惹人瞩目的应该就是那块(小小的)触屏了吧。

新款设备用全新的 Touch Bar 替代了原有的功能键,它们可扩展、支持多点触控,更重要的是,Touch Bar 对开发者们完全开放,这意味着你的 macOS app 可以获得一种全新的交互方式。

如果你是一个 macOS 开发者,你一定很希望自己的 app 能够立刻使用上这项前沿科技。在这个教程中,我会将向你们展示如何使用全新的 NSTouchBar API 来为你的 macOS app 创建一个动态的 Touch Bar。

注意:这个教程需要 Xcode 8.1 或更高版本以及 macOS 10.12.1 (Build 16B2657) 或更高版本,否则的话你将无法运行 Touch Bar 模拟器。你可以在 关于本机里点击数字版本号来查看 build 版本。


如果你的版本不够,你可以在 Apple 的网站上 下载。

Touch Bar 是个啥?

如上文所述,Touch Bar 是一块安装在键盘上方的细长形的触摸屏,它允许用户使用一种全新的方式来与 app(以及 Mac)进行交互。

在 Touch Bar 上有三个默认的部分:

  • 系统按钮:根据运行的 app,这里将会显示一个系统级的按钮,比如 esc;
  • App 区域:你的 app 可以控制的显示区域,也就是我们的主舞台;
  • 控制条:这里用于显示你熟悉的控制按钮,比如亮度、音量等。

和其它许多 Apple 的新科技一样,Touch Bar 也拥有自己的《人机交互则例(Human Interface Guidelines)》,为了你的 app 和其它 Mac app 拥有统一的用户体验,你应当遵循这份则例。你可以点击这里阅读它。

概括说来,这份则例中比较重要的几点有:

  • App 中的某个功能不应该只能在 Touch Bar 中使用:即使部份用户还未升级到最新的硬件,你的 app 也应该尽量为他们提供一样的功能。如果你决定为 Touch Bar 加入某些功能,请确保这个功能也能在 app 的其他某处也可以访问到。另外 Touch Bar 是可以被用户禁用的,所以也别太指望你的用户能一直看到它;
  • Touch Bar 是键盘的延伸,而不是一个显示屏:诚然,Touch Bar 是一个显示屏,但是它不是显示器的延伸,而是一个与 app 交互的窗口。你不应该在 Touch Bar 上用滚来滚去的内容或 blingbling 的警告打扰用户的视线;
  • 快速响应:用户在键盘上按下一个真实的按键时,按键会立即给予一个反馈(也就是被按下去了)。同理,用户在 Touch Bar 上按下一个虚拟按钮时,你也应该给他们提供即时的反馈。

如何让你的 app 支持 Touch Bar

要让你的 app 支持 Touch Bar,你需要使用 Apple 提供的两个类:NSTouchBarNSTouchBarItem(当然还有他们的子类)。

某些 NSTouchBarItem 的子类提供了这些功能:

  • Slider:滑动调节某个值;
  • Popover:把更多功能藏入一个二级菜单中;
  • Color Picker:和名字一样,用来选取颜色的咯🤷‍♀️;
  • Custom:这个子类是你的天下,你可以在它的里面塞入文本、按钮以及其他各种各样的控件。

从文字大小颜色到图片内容,你可以随意自定义你的 item,从而为你的用户提供一个传统键盘无法提供的、更加牛×的交互方式,但是请时刻请谨记《人机交互则例》。现在我们要开始动工啦。

准备开始

在开始敲代码之前,请先点击这里下载初始项目的源代码。

我们要编写的 app 是一个简单的旅行记录 app。打开初始项目,如果你的设备不支持 Touch Bar,请点击 Xcode 菜单栏上的 WindowShow Touch Bar,Touch Bar 的模拟器就会出现在屏幕上。

编译并运行你的 app,你将会看到 Touch Bar 上除了 esc 按钮和控制条以外空空如也。

我们要做的第一步是告诉系统我们的 app 需要自定义 Touch Bar。打开 AppDelegate.swift,将这些代码添加到 applicationDidFinishLaunching(_:) 方法中:

func applicationDidFinishLaunching(_ aNotification: Notification) {
  if #available(OSX 10.12.1, *) {
    NSApplication.shared().isAutomaticCustomizeTouchBarMenuItemEnabled = true
  }
}

这些代码将会帮你搞定启用 Touch Bar 所需要的各种操作,(在写这篇文章的时候)Xcode 还没有 macOS 10.12.1 的配套 SDK,所以你需要在 Touch Bar 相关的代码周边添加 #available(OS X 10.12.1, *),当然如果你忘了这件事,Xcode 会给你一个温馨的提醒😉。

打开 WindowController.swift,找到 makeTouchBar(),这个方法用于检测 ViewController 是否含有一个可以被返回的 Touch Bar,如果有,它会把这个 Touch Bar 返回给 Window,然后呈现给用户。现在,我们还没有创建 Touch Bar,所以什么也不会发生。

在你开始创建自己的 Touch Bar 和 Touch Bar Item 之前,你需要注意这些类都需要独一无二的 identifier(标识符),打开 TouchBarIdentifiers.swift,你将能看到两个扩展定义了一些标识符:NSTouchBarCustomizationIdentifierNSTouchBarItemIdentifier

前往 ViewController.swift,并把这些带码添加到文件最后 // MARK: - TouchBar Delegate 注释的后方:

@available(OSX 10.12.1, *)
extension ViewController: NSTouchBarDelegate {
  override func makeTouchBar() -> NSTouchBar? {
    // 1
    let touchBar = NSTouchBar()
    touchBar.delegate = self
    // 2
    touchBar.customizationIdentifier = .travelBar
    // 3
    touchBar.defaultItemIdentifiers = [.infoLabelItem]
    // 4
    touchBar.customizationAllowedItemIdentifiers = [.infoLabelItem]
    return touchBar
  }
}

在此处,通过重写 makeTouchBar() 方法,你给你的 View 或 Window 创建了一个 Touch Bar,在这个方法中:

  1. 创建一个新的 TouchBar 并设置它的 delegate;
  2. 设置 customizationIdentifier(自定义标识符),每个 TouchBarTouchBarItem 都需要一个独一无二的标识符;
  3. 设置 Touch Bar 的默认 item 标识符,这将会告诉 Touch Bar 它将会容纳哪些 item;
  4. 这一步是允许用户可以自定义的 item。作为参考,你可以打开 Finder,点击菜单栏上的 显示自定 Multi-Touch Bar看看自定义 Touch Bar 是什么效果。

接下来,我们还需要设置 .infoLabelItem 是什么样子的,在同一个 extension 中添加:

func touchBar(_ touchBar: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItemIdentifier) -> NSTouchBarItem? {
    switch identifier {
    case NSTouchBarItemIdentifier.infoLabelItem:
      let customViewItem = NSCustomTouchBarItem(identifier: identifier)
      customViewItem.view = NSTextField(labelWithString: "\u{1F30E} \u{1F4D3}")
      return customViewItem
    default:
      return nil
    }
}

通过实现touchBar(_:makeItemForIdentifier:) 方法,你可以自定义你的 Touch Bar Item,在段代码里,你创建了一个 NSCustomTouchBarItem,并把它的 view 设置为了 NSTextField

编译并运行你的 app,你可以看到 Touch Bar 多了一个 item。

耶✌️!通过一番努力你得到了……两个 emoji…好吧,现在我们来添加一些别的控件。

Text Field 和 Scrubber

makeTouchBar() 里,把 touchBar.defaultItemIdentifiers = [.infoLabelItem] 修改为:

touchBar.defaultItemIdentifiers = [.infoLabelItem, .flexibleSpace, .ratingLabel, .ratingScrubber]

这些代码会让 Touch Bar 显示三个 item:一个 label、一个 scrubber 以及一个 .flexibleSpace。这是一个动态的间距,它可以把各种 item 按组进行整洁的分类。此外你还可以使用 .fixedSpaceSmall.fixedSpaceLarge 这两个固定间距。

和之前的那个 label 一样,你也需要自定义这些 item,把这些 cases 添加到 touchBar(_:makeItemForIdentifier:) 里的 switch

case NSTouchBarItemIdentifier.ratingLabel:
  // 1
  let customViewItem = NSCustomTouchBarItem(identifier: identifier)
  customViewItem.view = NSTextField(labelWithString: "Rating")
  return customViewItem
case NSTouchBarItemIdentifier.ratingScrubber:
  // 2
  let scrubberItem = NSCustomTouchBarItem(identifier: identifier)   
  let scrubber = NSScrubber()
  scrubber.scrubberLayout = NSScrubberFlowLayout()
  scrubber.register(NSScrubberTextItemView.self, forItemIdentifier: "RatingScrubberItemIdentifier")
  scrubber.mode = .fixed
  scrubber.selectionBackgroundStyle = .roundedBackground
  scrubber.delegate = self
  scrubber.dataSource = self
  scrubberItem.view = scrubber
  scrubber.bind("selectedIndex", to: self, withKeyPath: #keyPath(rating), options: nil)    
  return scrubberItem

一步一步来看:

  1. 为「评分」新建了一个新的 label item;
  2. 然后创建一个新的 item 用来展示 NSScrubber。这是一个 Touch Bar 特有的控件,它允许你在若干个项目中进行选择。Scrubber 需要一个代理来处理事件,你需要做的是设置一个 delegate(初始项目的源代码已经在 ViewController 里实现过这个协议了)。

编译并运行你的 app,你将会看到 Touch Bar 里多出了两个新的 item,当你点击某个 scrubber 里的项目后,在 app 的主窗口里能看到数值的调整。

Segmented Control

接下来,我们来添加一个 Segmented Control。这个控件没有使用 Delegate 模式,因此这刚好可以让你体验一下怎么设置 Touch Bar 里的 Target-Action(目标动作)。回到 makeTouchBar() 中,把这三个 item 添加到 defaultItemIdentifiers 里:

touchBar.defaultItemIdentifiers = [.infoLabelItem, .flexibleSpace, .ratingLabel, .ratingScrubber, .flexibleSpace, .visitedLabelItem, .visitedItem, .visitSegmentedItem]

以及把最后的三个 case 添加到 touchBar(_:makeItemForIdentifier:) 中:

case NSTouchBarItemIdentifier.visitedLabelItem:
  // 1
  let customViewItem = NSCustomTouchBarItem(identifier: identifier)
  customViewItem.view = NSTextField(labelWithString: "Times Visited")
  return customViewItem
case NSTouchBarItemIdentifier.visitedItem:
  // 2
  let customViewItem = NSCustomTouchBarItem(identifier: identifier)
  customViewItem.view = NSTextField(labelWithString: "--")
  customViewItem.view.bind("value", to: self, withKeyPath: #keyPath(visited), options: nil)
  return customViewItem
case NSTouchBarItemIdentifier.visitSegmentedItem:
  // 3
  let customActionItem = NSCustomTouchBarItem(identifier: identifier)
  let segmentedControl = NSSegmentedControl(images: [NSImage(named: NSImageNameRemoveTemplate)!, NSImage(named: NSImageNameAddTemplate)!], trackingMode: .momentary, target: self, action: #selector(changevisitedAmount(_:)))
  segmentedControl.setWidth(40, forSegment: 0)
  segmentedControl.setWidth(40, forSegment: 1)
  customActionItem.view = segmentedControl
  return customActionItem

一步一步看:

  1. 和之前一样,创建一个简单的 Label;
  2. 创建另一个 Label,但这一次使用 bind 来把 Label 要显示的文本绑定到一个属性上;
  3. 最后,创建一个 Segmented Control,并设置它的 action。你可以看到,为它设置一个 action 和其他常见控件是完全一样的。

编译并运行,除了 Scrubber,你现在还可以和 Segmented Control 交互了。此外,在 Touch Bar 里修改一个数值,app 的主窗口中也会显示出来,反之亦然。

彩色按钮

能让用户使用 Touch Bar 来进行「保存」操作是一个不错的点子,因为这个按钮和别的按钮都不一样,我们可以把它设置成绿色的。你可以使用 NSButtonbezelColor 属性来给它设置一个特殊的颜色。

打开 TouchBarIdentifiers.swift,在 NSTouchBarItemIdentifier extension 里,添加这些代码:

static let saveItem = NSTouchBarItemIdentifier("com.razeware.SaveItem")

这将会从头开始创建一个标识符,以此允许你在 Touch Bar 里添加一个 item。

返回 ViewController.swift,添加一个新的 .flexSpace.saveItem 到 Touch Bar 的 defaultItemIdentifiers 里:

touchBar.defaultItemIdentifiers = [.infoLabelItem, .flexibleSpace, .ratingLabel, .ratingScrubber, .flexibleSpace, .visitedLabelItem, .visitedItem, .visitSegmentedItem, .flexibleSpace, .saveItem]

基本上完成了,最后一步是对按钮进行一些设置。在 touchBar(_:makeItemForIdentifier:) 中添加最后一个 case

case NSTouchBarItemIdentifier.saveItem:
  let saveItem = NSCustomTouchBarItem(identifier: identifier)
  let button = NSButton(title: "Save", target: self, action: #selector(save(_:)))
  button.bezelColor = NSColor(red:0.35, green:0.61, blue:0.35, alpha:1.00)
  saveItem.view = button
  return saveItem

通过 bezelColor,你已经把这个按钮成功地修改成了绿色。

编译并运行,你会看到 Touch Bar 上有了一个绿色的按钮,它和窗口中的 Save 按钮拥有一样的功能。

现在该做些啥?

你可以点击这里下载最终完成的项目。

这些就是 Touch Bar 的基础了。显然,Apple 希望 Touch Bar 的开发过程越简单越好,因此你可以尽快把这些特性尽快地带给新 MacBook Pro 的用户。

在这个教程中,你学到了:

  1. 如何设置你的 app,让它能显示 Touch Bar;
  2. 如何在 Touch Bar 里显示静止的 Label;
  3. 如何使用 binding(绑定)来添加一个动态的 Label;
  4. 如何在 Touch Bar 中添加控件,并处理它们的事件。

请不要在此停下!NSTouchBarNSTouchBarItem 中还有很多值得探索的特性。你可以试着在 Touch Bar 里添加一个 Popover,或者也可以试试在你的 app 里格式化一个文本,你还可以在试着去在 Interface Builder 里创建一个 Touch Bar。

How to Use NSTouchBar on macOS

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342

推荐阅读更多精彩内容

  • [译] 零基础 macOS 应用开发(二) 本文翻译自 raywenderlich.com 的 macOS 开发经...
    SR2k阅读 3,610评论 1 3
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,364评论 25 707
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,010评论 4 62
  • 李子+鸡肉,还有一个不知名的凉性菜,这个六月的最后一周感受了断食,当然,原因是拉肚子,第一天将近十次,水状,第二天...
    团的花园阅读 261评论 0 0
  • 老家的蓝天是具有独特气质的。天气晴朗时碧空如洗,蓝的让人陶醉,像是精美水粉画;偶尔飘着几多白云,洁润鲜柔,...
    寨泉阅读 1,046评论 1 7