したがって、これに対する答えは、気弱な人向けではありません。その理由は、NSTableView に自然にやりたくないことをさせようとしているからです。
cocoa のネイティブ NSTableView 複数選択動作を使用することから始めます。
次に、チェックボックスの列を追加します。この行では、ルールが異なります: - チェックボックスをクリックすると、その行のみの選択状態が切り替わります
チェックボックスへのクリックをキャプチャして自分で処理できれば、これは簡単です。今はできますが、問題は、それらを処理した後も NSTableView に転送され、通常の方法で選択が変更されることです。[注: この転送を回避する方法があるかもしれません - 知っている場合はお知らせください]
したがって、これを (最終的に) 達成する方法は次のとおりです。 - 基になるオブジェクト配列の各オブジェクトに「selectedInView」フィールドを追加します。keyPath: "selectedObjects" の関連付けられた NSArrayController にオブザーバーを追加します。選択が変更されたら、各オブジェクトに応じて selectedInView フィールドを設定します。このようなもの:
if([keyPath isEqualToString:@"selectedObjects"]) {
// For table view checkbox's: keep "selectedInView" of song dictionaries up to date
[_arrayController.arrangedObjects enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
BOOL sel = [_arrayController.selectedObjects containsObject:obj];
if([[obj objectForKey:@"selectedInView"] boolValue] != sel)[obj setValue:[NSNumber numberWithBool:sel] forKey:@"selectedInView"];
}];
ここで、注意が必要な部分があります。チェックボックスが誤動作するのは、既に選択項目が存在する場合のみです。ケースの種類は次のとおりです。
セットアップ: 行の 1、2、3 が選択されています。行 4 のチェックボックスがクリックされました。結果: 行 4 のチェックボックスが選択されます。行 4 が選択されます。行の 1、2、3 は選択解除されます (これは NSTableView が自然に行うためです)
これを解決するには、チェックボックスがクリックされるたびに、現在の選択を記憶する一時的な配列を作成する必要があります。クリックされたばかりのチェックボックスをプラスまたはマイナスします。
- (void)tableView:(NSTableView *)tableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
if([tableColumn.identifier isEqualToString:@"CheckBox"]) {
NSMutableDictionary *song = [_arrayController.arrangedObjects objectAtIndex:row];
if(!_tempSelectedSongs && _arrayController.selectedObjects) _tempSelectedSongs = [[NSMutableArray arrayWithArray:_arrayController.selectedObjects] retain];
if(_tempSelectedSongs) {
if([_tempSelectedSongs containsObject:song]) {
[_tempSelectedSongs removeObject:song];
} else if(![_tempSelectedSongs containsObject:song]) {
[_tempSelectedSongs addObject:song];
}
}
}
}
テーブルビューが選択処理を行った後、選択をあるべきものに設定したいと思います。実際に選択を行う前に、テーブルビューの選択を変更できる有望な機能があります。次のように変更できます。
- (NSIndexSet *)tableView:(NSTableView *)tableView selectionIndexesForProposedSelection:(NSIndexSet *)proposedSelectionIndexes {
NSMutableIndexSet *newSet = [NSMutableIndexSet indexSet];
if(_tempSelectedSongs) {
NSMutableIndexSet *indexSet = [NSMutableIndexSet indexSet];
[_tempSelectedSongs enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSUInteger index = [_arrayController.arrangedObjects indexOfObject:obj];
if(index != NSNotFound) [indexSet addIndex:index];
}];
proposedSelectionIndexes = indexSet;
[_tempSelectedSongs release]; _tempSelectedSongs = nil; [_tempSelectedSongsTimer invalidate]; [_tempSelectedSongsTimer release]; _tempSelectedSongsTimer = nil;
}
[proposedSelectionIndexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
NSProgressIndicator *progress = ((BDDiscreteProgressCell *)[[_arrayController.arrangedObjects objectAtIndex:idx] objectForKey:@"BDCell"]).progress;
if(!progress)
[newSet addIndex:idx];
}];
return newSet;
}
これはうまく機能しますが、NSTableView デリゲート関数が呼び出される順序に問題があります。明らかに、一時配列をセットアップする最初の関数が、情報を使用する2番目の関数の前に呼び出される必要があります。
何らかの理由で、チェックボックスを選択解除すると、これが機能することがわかりますが、チェックボックスを選択すると、反対のことが起こります。したがって、この場合、上記の keyPath オブザーバーにさらにコードを追加できます。
if([keyPath isEqualToString:@"selectedObjects"]) {
if(_tempSelectedSongs) {
_arrayController.selectedObjects = _tempSelectedSongs;
[_tempSelectedSongs release]; _tempSelectedSongs = nil;
}
// For table view checkbox's: keep "selectedInView" of song dictionaries up to date
[_arrayController.arrangedObjects enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
BOOL sel = [_arrayController.selectedObjects containsObject:obj];
if([[obj objectForKey:@"selectedInView"] boolValue] != sel)[obj setValue:[NSNumber numberWithBool:sel] forKey:@"selectedInView"];
}];
}
編集:追加のケースがあることが判明しました:単一の行が選択され、そのチェックボックスが「クリックされていない」場合、これは自動的に selectedObjects 通知をトリガーしないため、タイマーで関数を実行して新しい選択を実装する必要があります。