[Xcode 8.1] サンプルコードからTouchBarのAPIを理解する#1 TouchBarの概要とサンプルコードの動かし方
新しいMacBook Pro出ましたね
こんにちは!モバイルアプリサービス部の加藤 潤です。
先日発表された新しいMacBook ProにはTouch Bar
と呼ばれる新しい入力デバイスが搭載されました。
一時、「escキーが無くなったらどうしよう...?」と話題になりましたが、escキーが使えなくなるわけではない事がわかりホッとした人も多いかと思います。
さて、このTouch Barですが、現在使っているアプリ(コンテキスト)によって表示するコントロールを変えられるということで、今回はAppleのサンプルコードからTouch BarのAPIの使い方を見ていきたいと思います。
まずはTouch Barについて知る
まずはTouch Barについて概要を理解しましょう。こういう時は公式ドキュメントを読むのが一番です。macOS Human Interface Guidelines を見て見ます。
Touch Barとは
上記公式ドキュメントによると、Touch Barとは、「(Touch Barを)サポートしているMacBook Proのモデルでキーボードの上に表示されるRetinaディスプレイで、メインスクリーン上のコンテンツとやり取りを行うための動的なインターフェースコントロールを提供する入力デバイス」と書かれています。つまり、その時の状況に応じて適切なコントロールがRetinaディスプレイに表示され、かつディスプレイなのでタッチで操作できると読み取れます。
具体的な使用例として以下のようなケースが挙げられています。
- ドキュメント作成時のテキスト入力中にフォントやフォントサイズを変える
- 地図を表示中にワンタップで近くのガソリンスタンドやホテル、レストランにアクセスする
- Touch IDセンサーによるログインやApp StoreやApple Payの支払い
ここまで読んだだけでもなんだか便利そうですね!
Touch Barの構成要素
引き続きドキュメントを見てみると、ドキュメントにはこうあります。
「デフォルトでは、Touch Barの右側にはSiriの起動やメインディスプレイの明るさ調整、ボリューム調整など、システムレベルのタスクを実行するためのコントロールが表示されます。」
「システムレベルのコントロールの左横のアプリ領域内にアプリ固有のコントロールを実装することができます」
「Escボタンやその他のシステムによって提供されるボタンはコンテキストによってアプリ領域の表示されます。」
つまり、Touch Barには以下のそれぞれの用途用に以下の領域があるようです。
- System button
- Escボタンやその他のシステムによって提供されるボタンを表示する領域
- App region
- アプリ固有のコントロールを表示する領域
- Control Strip
- Siriの起動やメインディスプレイの明るさ調整など、システムレベルのタスクを実行するためのコントロールが表示される領域
さらに、Touch Barはユーザーが特定のコントロールを非表示にしたり、追加したりカスタマイズできると書かれています。
ここまででざっくりとではありますが、Touch Barについて理解できました。
Appleのサンプルコード
AppleからTouch BarのサンプルとしてNSTouchBar Catalog が提供されています。 こちらを読んでAPIについて理解したいと思います。
開発環境
サンプルコードを動かすための環境は以下の通りです。
- システムのバージョン macOS 10.12.1 (16B2657)
- Xcode Version 8.1 (8B62)
App StoreからインストールしたmacOS 10.12.1(ビルドバージョン 16B2555)ではサンプルコードがクラッシュしてしまい動作しませんでした。サンプルコードを動かすにはこちらのアップデートが必要です。アップデートを適用すると、ビルドバージョンが16B2657となります。また、後述するTouch Barのシミュレータもビルドバージョンが16B2657以降でないと使えません。この件に関してはXcode 8.1 Release Notesにも記載されており、Touch Barを使ったアプリを開発する場合、このアップデートは必須です。
Xcode でTouch Barのシミュレータを表示するには
XcodeでTouch Barのシミュレータを表示するにはWindow > Show Touch Bar
を選択します。
選択すると以下のようなシミュレータが表示されます。 このスクリーンショットはちょうど画面キャプチャ時のものです。ちゃんとコンテキストに応じたコントロールが表示されることが確認できました。
サンプルコードを読み解く
AppDelegate.swift
AppDelegate.swift
の処理は以下のようになっています。
if NSClassFromString("NSTouchBar") != nil { NSApplication.shared().isAutomaticCustomizeTouchBarMenuItemEnabled = true }
NSClassFromString("NSTouchBar") != nil
の部分でTouch Barの機能が使えるかどうかを実行時に確認しています。そしてNSApplication.shared().isAutomaticCustomizeTouchBarMenuItemEnabled
にtrue
を設定することでアプリのメインメニューに「Customize Touch Bar」メニューを表示しています。試しにfalse
に設定したら当該メニューは表示されませんでした。
WindowController.swift
WindowController.swift
の処理は以下のようになっています。
fileprivate extension NSTouchBarCustomizationIdentifier { static let windowBar = NSTouchBarCustomizationIdentifier("com.TouchBarCatalog.windowTouchBar") } fileprivate extension NSTouchBarItemIdentifier { static let label = NSTouchBarItemIdentifier("com.TouchBarCatalog.TouchBarItem.label") } class WindowController: NSWindowController { override func windowDidLoad() { super.windowDidLoad() self.window?.setFrameAutosaveName("WindowAutosave") } // MARK: NSTouchBar override func makeTouchBar() -> NSTouchBar? { let touchBar = NSTouchBar() touchBar.delegate = self touchBar.customizationIdentifier = .windowBar touchBar.defaultItemIdentifiers = [.label, .fixedSpaceLarge, .otherItemsProxy] touchBar.customizationAllowedItemIdentifiers = [.label] return touchBar } }
NSResponder
に定義されているmakeTouchBar() -> NSTouchBar?
という関数をオーバライドし、その中で以下を行なっています。
NSTouchBarの生成
- 1.で生成したオブジェクトのデリゲートを
self(WindowController)
に設定 customizationIdentifier
プロパティの型はNSTouchBarCustomizationIdentifier?
でTouch Barをカスタマイズ可能にするための識別子を設定します。- ここで設定している
.windowBar
は自分で定義したNSTouchBarCustomizationIdentifier
です。(この識別子はグローバルにユニークである必要があるため、逆ドメイン形式にしているようです。)
- ここで設定している
defaultItemIdentifiers
プロパティの型は[NSTouchBarItemIdentifier]
となっており、Touch Bar内の各アイテムに対する識別子を設定しています。.label
は自分で定義したNSTouchBarItemIdentifier
です。(この識別子もグローバルにユニークである必要があるため、逆ドメイン形式にしているようです。).fixedSpaceLarge
は標準で用意された識別子で名前から固定長の大きいスペースを表すことが想像できます。(.flexibleSpace
というのもあるのでおそらくこちらは可変長のスペースを表すのでしょう).otherItemsProxy
も標準で用意された識別子ですが、こちらはTouch Barを入れ子にするために必要なようです。
customizationAllowedItemIdentifiers
プロパティの型も[NSTouchBarItemIdentifier]
です。ここで指定した識別子のアイテムはカスタマイズのパレットに表示されるようです。
そして、NSTouchBarDelegate
の実装は以下のようになっています。
// MARK: NSTouchBarDelegate extension WindowController: NSTouchBarDelegate { func touchBar(_ touchBar: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItemIdentifier) -> NSTouchBarItem? { switch identifier { case NSTouchBarItemIdentifier.label: let custom = NSCustomTouchBarItem(identifier: identifier) custom.customizationLabel = "TouchBar Catalog Label" let label = NSTextField.init(labelWithString: "Catalog") custom.view = label return custom default: return nil } } }
アイテムの識別子が.label
の場合にNSCustomTouchBarItem
を生成し、中のビューとしてNSTextField
を代入しています。
この部分でアプリ固有のアイテムを定義しているようです。
実行結果
今回ご紹介したコード部分を実行した結果、Touch Barは以下のようになりました。
また、「Customize Touch Bar」メニューをクリックして表示したカスタマイズのパレットは以下のようになりました。
Catalog
と表示されたアイテムはカスタマイズ可能なので非表示にできたり、位置を変えたりできました。fixedSpaceLarge
のアイテムについては非表示にはできましたが、ドラッグして位置を入れ替えることはできませんでした。(ただし、Catalog
アイテムの方をドラッグすれば位置を入れ替えることが可能)
defaultItemIdentifiers
に設定したアイテムがカスタマイズパレット上ではDefault Set
として表示され、いろいろとカスタマイズしてもいつでもTouch Barの状態をこのデフォルトに戻すことができるようです。
ちなみに、customizationIdentifier
に何も設定しないと、isAutomaticCustomizeTouchBarMenuItemEnabled
にtrue
を設定してもアプリのメインメニューに表示された「Customize Touch Bar」メニューがdisabled状態になり、クリックできない(=カスタマイズできない)状態になりました。
また、customizationAllowedItemIdentifiers
についても何も設定しないと、同様に「Customize Touch Bar」メニューがdisabled状態になりました。
このことから、カスタマイズ可能なTouch Barにしたい場合はcustomizationIdentifier
とcustomizationAllowedItemIdentifiers
の両方を設定しないといけないことがわかりました。
おわりに
今回はAppDelegate.swift
とotherItemsProxy.swift
のソースコードからざっくりとやっていることを理解しました。
引き続きサンプルコードを理解してまいります。