NSTableViewで行選択をしたときに呼ばれるデリゲートメソッドは以下の4つ。
- (BOOL)selectionShouldChangeInTableView:(NSTableView *)aTableView
- (BOOL)tableView:(NSTableView *)aTableView shouldSelectRow:(NSInteger)rowIndex
- (void)tableViewSelectionIsChanging:(NSNotification *)aNotification
- (void)tableViewSelectionDidChange:(NSNotification *)aNotification
基本的には上記の順に呼ばれ、このうち前の2つが選択変更をしてもよいかどうか判定するメソッドでどちらか一方でもNOを返せば、それ以後のメソッドは呼ばれない。後の2つは選択実行時に呼ばれ、変更を取りやめることはできない。
上記メソッドが下記それぞれの状況でどのように呼ばれるかを確認する。
(A)最初の許可メソッドで不許可(NO)を返す場合
-selectionShouldChangeInTableView:
がNOを返す時、なぜかこのメソッドが2回呼ばれる。
データのない行をクリックするなどして全非選択(empty)にする場合には1回だけ。
他のメソッドは呼ばれない。
(B)最初の許可メソッドが許可(YES)を返すが、二番目の許可メソッドは不許可(NO)を返す場合
- selectionShouldChangeInTableView:
- tableView:shouldSelectRow:
- selectionShouldChangeInTableView:
- tableView:shouldSelectRow:
とやはり2回づつ呼ばれている。以後のメソッドは同じく呼ばれない。
(C)許可メソッドが許可(YES)を返す場合
2つの許可メソッドが共にYES,YESを返す場合には
- selectionShouldChangeInTableView:
- tableView:shouldSelectRow:
- tableViewSelectionIsChanging:
- tableViewSelectionDidChange:
が順に呼ばれる。
(D)追加行選択の場合
複数行選択(multiple)を認めている場合、二つ目以降の行を選択しようとすると以下の順に呼ばれる。
- selectionShouldChangeInTableView:
- tableView:shouldSelectRow:
- tableView:shouldSelectRow:
:
- tableViewSelectionIsChanging:
- tableViewSelectionDidChange:
tableView:shouldSelectRow は、選択済みの行も新たに追加選択した行も含めてすべての行について呼ばれることになる。
(E)追加行の許可メソッドが不許可(NO)を返す場合
新たに追加した行がすべてNOを返す場合には以下のようになる。
- selectionShouldChangeInTableView:
- tableView:shouldSelectRow:
- tableView:shouldSelectRow:
:
tableView:shouldSelectRowは全ての行について呼ばれるが、追加選択される行がなければ以後のメソッドは呼ばれない。
(F)選択なし(empty)になるとき
選択状態から選択行がなくなる場合には以下。
- selectionShouldChangeInTableView:
- tableViewSelectionIsChanging:
- tableViewSelectionDidChange:
個別行の選択許可を求めるメソッドが呼ばれない。
(G)一気に複数行選択した場合
複数行を(SelectAllなどで)同時にまとめて選択した場合には以下。
- selectionShouldChangeInTableView:
- tableView:shouldSelectRow:
- tableView:shouldSelectRow:
:
- tableViewSelectionDidChange:
tableView:shouldSelectRowは選択しようとした行数分だけ呼ばれるのだが、..IsChanging:が呼ばれなくなる。
(H)複数選択中に選択解除する行がある時
- selectionShouldChangeInTableView:
- tableViewSelectionIsChanging:
- tableViewSelectionDidChange:
tableView:shouldSelectRowはもちろん呼ばれないが、..IsChanging:や..DidChanged:は呼ばれる。
tableView:shouldSelectRowは選択しようとした行数分だけ呼ばれるのだが、..IsChanging:が呼ばれなくなる。
(I)一気に複数行選択解除した場合
Gの逆。
- selectionShouldChangeInTableView:
- tableViewSelectionIsChanging:
- tableViewSelectionDidChange:
(*)データ
ノーティフィケイションメソッドはobjectがNSTableViewになっているが、userInfoには何もセットされていない。
また、 NSTableView -selectedRowIndexes はshould..では選択される前の状態で選択された行を含んだ選択行番号の組は..IsChnaging:以降で取得できるようになる。
つまり、selectionShouldChangeInTableView:ではどの行が選択されるのかわからない状態でYES/NOを判断する必要がある。
まとめ
上記のことから各メソッドの役割を考察する。
- (BOOL)selectionShouldChangeInTableView:(NSTableView *)aTableView
あらたに選択する、選択を追加する、選択を(一部または全部)解除する場合など、とにかく選択状態が変わる時に「変えていいかどうか」確認するために呼ばれる。
とくに選択解除時にはどの行が解除されるか分からないことに加え、もうひとつのメソッドは呼ばれないのだから、複数行のうち選択解除してよい行と選択解除できない行が混在している場合でも選択解除不可としてNOを返さなければならない。
選択解除されようとしている行(クリックされた行)が分かればその行が選択解除OKかどうかで判断を変えることができるかもしれない。※10.5以降の注意を参照
- (BOOL)tableView:(NSTableView *)aTableView shouldSelectRow:(NSInteger)rowIndex
変更後に選択状態になる行すべてについて呼ばれる。既選択の行も呼ばれる。ここではじめて個別行について選択してよいかどうかの判定がかのう。選択解除される行については呼ばれない。
- (void)tableViewSelectionIsChanging:(NSNotification *)aNotification
選択状態を変更中(?)に呼ばれるメソッド。新たに選択した場合、解除した場合の両方で呼ばれるが複数行がまとめて選択される状況では呼ばれない。だが複数行がまとめて解除される場合には呼ばれる。謎が多いメソッド。
- (void)tableViewSelectionDidChange:(NSNotification *)aNotification
選択状態を変更した後に呼ばれる。あらたに選択された行がある場合、選択解除された行がある場合のいずれでも呼ばれる。
10.5以降の注意
10.5で以下のデリゲートメソッドが追加されている。
- (NSIndexSet *)tableView:(NSTableView *)tableView selectionIndexesForProposedSelection:(NSIndexSet *)proposedSelectionIndexes
このメソッドがインプリメントされていると、tableView:shouldSelectRow: は呼ばれないでこちらのメソッドが呼ばれる。tableView:shouldSelectRow: を複数回呼ぶ変わりにこのメソッドが一回だけ呼ばれるようになっている。
そしてproposedSelectionIndexesに選択状態変更後に選択状態になる行番号の組がわたってくるのでこれと現在選択中の行-selectedRowIndexesを比較すれば選択解除されようとしている行が判明する。また選択される組み合わせでOK/NGを判定するような複雑な処理も可能になっている。