[Xcode 8.1] サンプルコードからTouchBarのAPIを理解する#4 NSTouchBar CatalogのFancy Groupの構造と処理

2016.11.15

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

本記事の目標:Fancy Groupのサンプルを理解する

こんにちは!モバイルアプリサービス部の加藤潤です。
前回に引き続きNSTouchBar Catalogを理解していきます。
今回はサンプルアプリの左側に表示されるメニューの内、Fancy Groupがどういう構造で成り立っているかを見ていきます。

実行結果

まず先にアプリとタッチバーの実行結果をご覧ください。

Fancy Group

fancy_group

ボタンが5つあり、「Button 1」, 「Button 2」, 「Button 3」, 「Button 4」をタップしても見た目上は何も起きませんが、
「Open Popover」をタップするとスライダーが表示されるようになっています。 また、スライダー左横の×ボタンをタップすると、スライダーが消えて再び5つのボタンが表示されます。

fancy_group_principal また、「Principal」のチェックを外すと全体が左に寄り、再びチェックを入れると元の位置に戻ることが確認できます。

Storyboardの構造

FancyGroupViewController.storyboardの中がどういう構造になっているかを見てみましょう。

fancygroupviewcontroller_storyboard

枠で囲った部分を見てください。
NSTouchBarの中にNSGroupTouchBarItemがあり、その下にさらにNSTouchBarがあります。
この部分は前回ご紹介したGroupViewController.storyboardと同じ作りになっています。
また、Button 1, Button 2, Button 3, Button 4についてもGroupViewController.storyboardと同様にNSButtonが配置されています。
前回と異なるのはNSPopoverTouchBarItemが配置されている点です。
NSPopoverTouchBarItemの中にさらにNSTouchBarが配置されていて、その中がNSTouchBarItemNSViewController及びNSSliderがぶら下がる形になっています。

nspopovertouchbaritem_shows_close_button

NSPopoverTouchBarItemAttributes inspectorを見てみると、Shows Close Buttonという項目があります。
この部分で×ボタンの表示/非表示を設定できるようです。試しにこの部分のチェックを外してみたら×ボタンが表示されませんでした。
非表示にするケースが思いつきませんが...

ソースコード

次にFancyGroupViewController.swiftを見てみましょう。

fileprivate extension NSTouchBarItemIdentifier {
    static let fancyGroup = NSTouchBarItemIdentifier("com.TouchBarCatalog.TouchBarItem.fancyGroup")
}

class FancyGroupViewController: NSViewController {
    // MARK: Action Functions

    @IBAction func buttonAction(_ sender: AnyObject) {
        let button = sender as! NSButton
        print("\(#function): button with title \"\(button.title)\" is tapped!")
    }

    @IBAction func sliderChanged(_ sender: AnyObject) {
        let slider = sender as! NSSlider
        print("\(#function): \"\(slider.intValue)\" !")
    }

    @IBAction func principalAction(_ sender: AnyObject) {
        guard let checkBox = sender as? NSButton else { return }

        let identifier = checkBox.state == NSOnState ? NSTouchBarItemIdentifier.fancyGroup : nil
        touchBar?.principalItemIdentifier = identifier
    }
}

1〜3行目

NSTouchBarItemIdentifierを定義しています。
ここで定義したcom.TouchBarCatalog.TouchBarItem.fancyGroupという識別子がFancyGroupViewController.storyboard内のNSGroupTouchBarItemのIdentifierに設定されています。

8〜11行目

Button 1, Button 2, Button 3, Button 4がタップされた時にどのボタンがタップされたかを出力しているだけです。

13〜16行目

スライダーの値を変化させた時にその時の値(0~100の値)を出力しているだけです。

18〜23行目

「Principal」のチェックのON/OFF時に呼ばれるアクションメソッドです。チェックがONの場合はtouchBar?.principalItemIdentifierに1〜3行目で定義したNSTouchBarItemIdentifier(=NSGroupTouchBarItemのIdentifier)を設定し、チェックがOFFの場合は当該プロパティにnilを設定しています。
principalItemIdentifierについては、NSTouchBarのAPI Referenceに以下のように書かれています。

If you need to center an item in the Touch Bar, designate it as a principal item by assigning it to its bar’s principalItemIdentifier property. Do not hard-code spacing in an attempt to ensure an item is centered. If you want a group of items to appear centered in the Touch Bar, designate the group NSTouchBarItem as the principal item.

タッチバー内でアイテムをセンタリングしたい場合に使うプロパティのようです。「アイテムのグループをセンタリングしたい場合はNSGroupTouchBarItemをprincipal itemに設定してください」とありますが、今回のサンプルがまさにそうなっていますね。

改めて最初の実行結果を見てみると、チェックが入った状態だと確かにグループ(ボタン全体が)中央に表示されています。チェックを外すと左寄りになるのはアイテムの配置がそうなっているからです。

まとめ

今回はFancy Groupのサンプルがどういう構造で成り立っているか、どういう作りになっているかを学びました。
今回のポイントは以下です。

  • NSPopoverTouchBarItemを使うと一時的に表示するタッチバーを表現することができる。
  • principalItemIdentifierを使うと、タッチバー内でアイテムをセンタリングすることができる。

参考

NSTouchBar