見出し画像

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

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

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

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

"Use Storyboards"と"Create Document-Bassed Application"を選択する。

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

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

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

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

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

ViewControllerの親にNSTableViewDataSourceとNSTableViewDelegateを追加する。

class ViewController: NSViewController, NSTableViewDataSource, NSTableViewDelegate {
   ...
}

行数を返す。

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

欄の内容をビューで返す。

 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
   }

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

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

行数を返すのは同様だ。

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

欄の内容を返すのも似ている。

- (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;
}

ただ、メソッドの戻り値の型がidだ。どういう事なのだろうか?

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

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

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

ソースコード
GitHubからどうぞ。
https://github.com/murakami/workbook/tree/master/mac/VisiCalc - GitHub

【関連情報】
- Table View Programming Guide for Mac
- Cocoa.swift 2019-03
- Cocoa.swift 2019-04
- Cocoa.swift
- Cocoa勉強会 関東
- MOSA
- Cocoa練習帳
- Qiita

この記事が気に入ったらサポートをしてみませんか?