所以,這個問題的答案是不適合心臟虛弱。原因是你正在試圖讓NSTableView做一些它自然不想做的事情。
利用可可的本地NSTableView的多重選擇行爲開始了: - 點擊一行選中它並取消選擇其他行 - 保持控制,並單擊列切換該行的選擇狀態,只有
現在,添加一列複選框。對於這一行,規則是不同的: - 單擊複選框只會切換該行的選擇狀態
如果我們可以捕獲對複選框的點擊並自行處理,這將很容易。現在我們可以,但問題是,在我們處理它們之後,它們仍然會被轉發到NSTableView,以通常的方式改變選擇。 [注意:可能有一些方法可以避免這種轉發 - 如果你知道一個,請告訴我]
所以你可以(最後)如何完成這個: - 爲每個對象添加一個「selectedInView」字段在底層對象數組中。爲keyPath添加一個觀察者到關聯的NSArrayController:「selectedObjects」。當選擇改變時,相應地爲每個對象設置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行的複選框。第四行被選中。 Row的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];
}
}
}
}
現在在tableview完成它的選擇處理後,我們要設置選擇它應該是什麼。有一個看起來很有前途的功能,允許您在實際進行選擇之前修改tableview選擇。你可以修改它,如下所示:
- (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的委託函數被調用的順序有問題。顯然我們需要第一個函數 - 我們在那裏設置臨時數組 - 在第二個函數之前調用 - 我們使用這些信息。
無論出於何種原因,事實證明,當你DE選擇一個複選框,這是事情的工作原理,但當你選擇一個複選框時,會發生相反的情況。因此,對於這種情況下,你可以添加更多的代碼到你上面的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通知,因此您必須在計時器上運行一個函數來實現新的選擇。