テーブルビューの基本(NSTableView)
macOS Mojava 10.14.6 / Xcode 11.3.1 / Swift 5.0
![[image1.png]](image1.png)
テーブルビューの構造
テーブルビューオブジェクトは関連するオブジェクトの複合体である。中核となるクラスは NSTableViewクラスで、テーブルビューの各要素に対応する複数のクラスから構成される。また NSTableViewクラスはビューのスクロールを制御する NSClipViewと NSScrollViewクラスに包まれる。
![[image11.png]](image11.png)
NSTableColumnオブジェクトはテーブルの列を表し、その中に表の個々のデータを表す NSTableCellViewオブジェクトをテーブルの行数分だけ持つ。これは一般的にセルと呼ばれるものに相当する。NSTableCellViewオブジェクトは中に NSTextFieldを持ち、データとして表示する文字列を表す。NSTableHeaderCellは、個々の列の見出しを表す。
NSTableHeaderViewは、見出し行を表すオブジェクト、NSTableRowViewは表の一行一行を表すオブジェクトで、これらは行を一つのオブジェクトとして扱うときに利用する。
テーブルビューオブジェクトを作成する
インタフェースビルダにより、xibファイルに TableViewオブジェクトを追加したとき、テーブルビューを構成するオブジェクト群が自動的に生成される。
![[image2.png]](image2.png)
プロパティの設定
Interface Builderからプロパティを設定する。(プログラムからの設定も可能)
-
Content Modeプロパティの指定
テーブルビューにデータを表示する方式を View Based または Cell Basedから選択できる。View Basedはデータとして NSViewオブジェクト(属性付き文字列、画像、ボタンなどのコントロール類)を表示できるもので、一般的にはこれを指定すればよい。Cell Basedは古い処理方式で表示できるのは通常の文字列だけである。
-
Columnsプロパティの指定
列数を指定する。指定した数の列オブジェクト(NSTableColumnクラス)が自動的に作成される。
-
列に識別名を付与する
テーブルビューの列(NSTableColumnオブジェクト)の識別名(idetifireプロパティ)にユニークな名前をつける。これはデータソースとテーブルビューの列を結びつけるときのキーとなる。本例では1列を "col1"、2列を "col12"、3列を "col3" としている。また、列見出しの文字列は、title属性に記述する
![[image3.png]](image3.png)
データソースを作成する
テーブルビューに表示するデータはどのような形式のデータでも構わないが配列を使うことが多い。列が複数の場合は、配列の要素に配列または辞書を定義する。
本例では2次元配列をデータソースとする。1次元目の配列が行に、その中の要素が行の中のセルに相当する。
デリゲート&データソースメソッドを実装する
テーブルビューのデータは、いわゆるデリゲートパターンにより表示される。テーブルビューオブジェクトは、テーブルビューにデータを表示する必要が生じたとき、NSTableViewDataSourceプロトコルに準拠したデリゲートメソッドを自動的に起動する。
クラス宣言でデリゲートメソッドを実装することを宣言する。
NSTableViewオブジェクトと接続する
numberOfRows ( in: ) メソッドの実装
テーブルビューのレコード数を返す。
tableView ( _: viewFor: row: ) メソッドの実装
テーブルの各セルに表示するデータを返す。メソッドの引数は列オブジェク(col)と行番号(row)で、これにより表の行列の位置が特定できる。この位置に表示するデータをメソッドの戻り値とする。
戻り値の型は NSTableCellViewクラスである。戻り値となるオブジェクトは makeViewメソッドにより作成する。これにより作成したオブジェクトは使い回されるようなので、パフォーマンスの向上が見込まれる。このオブジェクトが保持する NSTextFieldオブジェクトに表示する文字列を設定する。
文字列以外のオブジェクトとして属性付き文字列、イメージ、ボタンなどを表示することもできる。具体的な方法については、テーブルビューの応用(4)セルにイメージ、ボタンを表示する を参照のこと
テーブルビューを表示する
reloadDataメソッドは、データソースからデータを読み込みテーブルビューを更新する。このメソッドは、まず、テーブルビューオブジェクトがロードされた時点で自動的に起動するが、データソースの値が変化してテーブルビューを再表示したいときにもプログラムから呼び出すことができる。
![[image1.png]](image1.png)
全ソースコード AppDelegate
これ以降、テーブルビューのカスタマイズ方法についていくつか紹介する
スクロールビューの高さを計算する
テーブルビューは保持する行がスクロールビューに全て収まらない場合はその中でスクロールする。スクロールビューには、その大きさに応じた行が表示されるが、できれば最後の行が途中で切れることなく収まるようにしたい。行の高さと行数からスクロールビューの適切な高さを求める計算式は次の通りとなる。
スクロールビューの高さ =(1行の高さ+セル間隔)× 行数 + ヘッダの高さ
各要素のプロパティ名とデフォルト値(単位ピクセル)
本例では 5行のレコードを表示するので、スクロールビューの高さを 120ピクセルとしている。Interface Builderで定義するか、プログラムからプロパティに設定する。
120 = (17 + 2) x 5 + 25
指定した行を選択状態にする
行番号(0〜)を IndexSetに変換して、selectRowIndexesメソッドを呼ぶ。(byExtendingSelection引数は真偽値のどちらを指定しても動作は同じ。用途不明)
1行目を選択状態にする
![[image7.png]](image7.png)
次の例は、データソースの最後の行を選択状態にするものである。テーブルビューに全件のレコードを表示することができない場合は scrollRowToVisibleメソッドにより最後の行がテーブルビューに現れるようにビューをスクロールさせる。
![[image8.png]](image8.png)
複数の行を選択状態にする
allowsMultipleSelectionプロパティを指定すれば、一度に複数の行を選択することができる。
![[image10.png]](image10.png)
行番号を取得するには selectedRowIndexesプロパティを参照する。この値は IndecSetという Indexを表す整数の集合で、mapメソッドにより整数の配列に変換することができる。
行が選択されたときに起動するデリゲートメソッド
任意の行をマウスでクリックすると、その行が青色反転し選択状態になる。また、そこから上下の矢印キーでカーソルを移動すると、移った先の行が選択状態になる。また、selectRowIndexesメソッドによりプログラムから指定した行を選択状態にできる。このとき起動するデリゲートメソッドが tableViewSelectionDidChangeである。
選択された行の行番号は NSTableViewオブジェクトの selectedRowプロパティから得られる。NSTableViewオブジェクトは、引数の Notificationオブジェクトから、またはインスタンス変数に代入されたオブジェクトから取得する。
見出し行をクリックすると、行番号は -1 になる。
列見出しをクリックしてデータをソートする
![[image5.png]](image5.png)
列見出しをマウスでクリックすると
デリゲートメソッド tableView( _: mouseDownInHeaderOf: ) が起動する。
mouseDownInHeaderOf引数に設定された NSTableColumnオブジェクトの identifierプロパティから、クリックされた列が判る。
列に対応したデータのソートを行い、reloadDataメソッドによりテーブルビューを再表示する。
次のコードは「列1」のヘッダをクリックすると、列1の値をキーにしてデータソースのレコードを並べ替えるものである。
ソートの方向(昇順/降順)は、現在表示中のレコードの並びから決定する。昇順に並んでいたら好順にソート、降順に並んでいたらその逆となる。また、ソートの方向を示した記号(▲ or ▼)をヘッダの見出しに表示する。
なお、テーブルビューの値は文字列なので、数値としてソートしたい場合は値をInt型に変換する必要がある。
[注意事項]
このメソッドの中で、指定した行を選択状態にする selectRowIndexesメソッドを実行しても正しく動作しない。上記のコードではソートの後に1行目を選択しようとしている。厳密には一瞬選択状態になるがすぐに消えてしまう。ただし列見出しのクリックをゆっくりするとうまくいく。原因は不明。
reloadDataメソッドの注意事項
awakeFromNibメソッドの中で reloadDataメソッドを実行してはならない。下図のように一部の行の文字列が逆さまに表示される場合がある。理由はわからない。
![[image4.png]](image4.png)