[cocoa][swift]テーブルビューを使ったmacOSアプリケーション

<p>macOSとiOSのUI関連のフレームワークには差異があり、前者はNeXTSTEPから受け継がれたスタイルとなっていて、後者は貧弱な計算機リソースでの利用を想定した設定となっている。</p>

<p>ただ、最近ではmacOSのフレームワークの方がiOSの方に合わせることが可能な部分については似せてきていて、テーブルビューの場合は昔からのCell BasedとiOSと同様なView Basedの二通りの方式があるという状況だ。前者については非推奨となっているため、後者についてサンプルを作りながら説明する。</p>

<p>新規プロジェクトを生成。macOSでCocoa Appを選択。</p>

<img width="369" alt="新規プロジェクト.png" src="https://qiita-image-store.s3.amazonaws.com/0/12540/1b2639be-994c-0f41-72dc-5253b502bf41.png">

<p>"Use Storyboards"と"Create Document-Bassed Application"を選択する。</p>

<img width="369" alt="プロジェクトの種類.png" src="https://qiita-image-store.s3.amazonaws.com/0/12540/15a8c13d-4dc2-c312-3ec2-732918cc761e.png">

<p>nib（ファイル名のsuffixはxib）でなくStoryboardを選んだ場合、iOSに似たViewControllerを利用した構成となる。iOS向けの開発経験者にとっては、その方が分かりやすいと思うが、macOSプログラミングの書籍の多くはnibの場合の説明となっているので、どちらが学習しやすいか悩ましい問題だ。ただ、今後はStoryboardだと思うので茨の道だがこちらの道を選択することにした。</p>

<p>Storyboardで、ViewにライブラリからTable Viewを配置し、TableViewのContent ModeがView Basedになっていることを確認する。</p>

<img width="536" alt="ViewBased.png" src="https://qiita-image-store.s3.amazonaws.com/0/12540/375dc17c-b736-6596-e311-90c67447f699.png">

<p>サンプルでは、テーブルの列を2にしたが、列に識別子をつけるのは大事だ。iOSの場合、列は1個のみなので、行番号となるインデックスのみで良かったが、macOSでは列を識別するものが必要だ。また、列の順番を変更することができるので、列インデックスでは対応できない。</p>

<img width="536" alt="列の識別子.png" src="https://qiita-image-store.s3.amazonaws.com/0/12540/29c45bc7-b412-85d8-697d-53875b1a3229.png">

<p>Storyboardを選ぶと、ビューコントローラが生成されるが、これとTable ViewをdataSourceとdelegateで繋げる。</p>

<img width="536" alt="繋げる.png" src="https://qiita-image-store.s3.amazonaws.com/0/12540/70e2e6fe-c2ba-8034-1fc1-fc5968680416.png">

<p>ターブルビューの行数や、表示する欄の設定は、iOSのUITableViewと似ている。</p>

<p>ViewControllerの親にNSTableViewDataSourceとNSTableViewDelegateを追加する。</p>

<pre><code>class ViewController: NSViewController, NSTableViewDataSource, NSTableViewDelegate {
    ...
}
</code></pre>

<p>行数を返す。</p>

<pre><code>    public func numberOfRows(in tableView: NSTableView) -> Int {
        return 64
    }
</code></pre>

<p>欄の内容をビューで返す。</p>

<pre><code>    public func tableView(_ tableView: NSTableView, viewFor
        tableColumn: NSTableColumn?, row: Int) -> NSView? {
        print(#function + " column:" +
            tableColumn!.identifier.rawValue + " row:" + String(row))
        var result: NSTextField? =
            tableView.makeView(withIdentifier:NSUserInterfaceItemIdentifier(rawValue:
                "MyView"), owner: self) as? NSTextField
        if result == nil {
            result = NSTextField(frame: NSZeroRect)
            result?.identifier =
                NSUserInterfaceItemIdentifier(rawValue: "MyView")
        }
        if let view = result {
            print("view:" + view.identifier!.rawValue + " row:" + String(row))
            if let column = tableColumn {
                if column.identifier.rawValue == "TableColumn1" {
                    view.stringValue = "column 01"
                }
                else if column.identifier.rawValue == "TableColumn2" {
                    view.stringValue = "column 02"
                }
            }
        }
        return result
    }
</code></pre>

<p>Storyboardで設定した識別子で列がわかるので、列に対応したビューを返している。</p>

<p>好奇心公正な人なら、非推奨だと分かっていても、以前のCell Basedが気になっていると思うので調べてみた。</p>

<p>行数を返すのは同様だ。</p>

<pre><code>- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
{
    NSInteger count=0;
    if (self.namesArray)
        count=[self.namesArray count];
    return count;
}
</code></pre>

<p>欄の内容を返すのも似ている。</p>

<pre><code>- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
{
    id returnValue=nil;
    NSString *columnIdentifer = [aTableColumn identifier];
    NSString *theName = [namesArray objectAtIndex:rowIndex];
    if ([columnIdentifer isEqualToString:@"name"]) {
        returnValue = theName;
    }
    return returnValue;
}
</code></pre>

<p>ただ、メソッドの戻り値の型がidだ。どういう事なのだろうか？</p>

<p>Cell Basedでは、ビューを管理するセルが存在する。デフォルトでは文字列を表示するビューが用意され、それを管理するセルに対して、値となるNSStringを返しているということのようだ。</p>

<p>複雑な欄を表示したい場合、それ様のセルを用意することになるが、それが一手間となるし、セルはビューでないので、ビューの重なりも独自に保持しないといけない。</p>

<p>大変そうなので、Cell Basedの探求はここまでにしておこう。</p>

ソースコード
GitHubからどうぞ。<br />
<a href="https://github.com/murakami/workbook/tree/master/mac/VisiCalc">https://github.com/murakami/workbook/tree/master/mac/VisiCalc</a> - GitHub<br />

【関連情報】
<a href="https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/TableView/Introduction/Introduction.html">Table View Programming Guide for Mac</a><br />
<a href="https://cocoa-kanto.connpass.com/event/121658/">Cocoa.swift 2019-03</a><br />
<a href="https://cocoa-kanto.connpass.com/event/124467/">Cocoa.swift 2019-04</a><br />
<a href="https://cocoa-kanto.connpass.com/">Cocoa.swift</a><br />
<a href="https://www.facebook.com/groups/cocoa.kanto/">Cocoa勉強会 関東</a><br />
<a href="https://mosa.connpass.com/">MOSA</a><br />
<a href="http://www.bitz.co.jp/weblog/">Cocoa練習帳</a><br />
<a href="https://qiita.com/m_yukio">Qiita</a><br />