2013-08-29 39 views
1

我有一個NSTableView的經典設置,其中的列綁定到NSArrayController的各個佈局對象的keyPaths,而NSArrayController綁定到一個字典數組。使用複選框來管理NSTableView中的選擇

使用此設置,在tableview中選擇一行或多行將自動生效。表視圖和數組控制器一起工作,並且很容易查詢NSArrayController以獲取選定對象的列表。

我的表格列之一包含NSButtonCells,複選框種類。有沒有辦法使用Cocoa Bindings來將每行中的複選框綁定到該行的選擇狀態?我知道我可以向代表每一行的NSDictionary添加另一個值,但是它會複製NSArrayController中已有的選擇信息。

如果有必要這樣做,也將欣賞您的實施快速簡介。

感謝

回答

1

所以,這個問題的答案是不適合心臟虛弱。原因是你正在試圖讓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通知,因此您必須在計時器上運行一個函數來實現新的選擇。