Таким образом, ответ на это не для слабонервных. Причина в том, что вы пытаетесь заставить NSTableView делать что-то, чего он, естественно, не хочет делать.
Начните с использованием родного NSTableView множественного поведения выбора Какао: - нажав на строку выбирает его и отменяет другие строки - проведение контроля и нажав на колонке переключает состояние выбора этой строки только
Теперь, добавьте столбец флажков. Для этой строки правила разные: - нажатие на флажке переключает только состояние выбора этой строки
Это было бы легко, если бы мы могли зафиксировать клики на флажках и обработать их самостоятельно. Теперь мы можем, но проблема в том, что после того, как мы их обработаем, они все равно передаются в NSTableView, изменяя выбор обычным способом. [Примечание: может быть какой-то способ избежать этой пересылки - если вы знаете об этом, сообщите мне об этом)
Итак, как вы можете (наконец) выполнить следующее: - добавить поле «selectedInView» каждому объекту в базовом массиве объектов. Добавьте наблюдателя в соответствующий NSArrayController для keyPath: «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. Результат: флажок в строке четвертый выбран. Выбирается четвертая строка. Строки 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 сделал это процесс выбора, мы хотим, чтобы установить выбор на то, что оно должно быть. Существует многообещающая функция, которая позволяет вам изменять выбор табличного представления, прежде чем он действительно выполнит выбор.Вы можете изменить его следующим образом:
- (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-флажок, вот как это работает: , но когда вы установите флажок, происходит обратное. Поэтому в этом случае, вы можете добавить еще некоторый код в ваш выше ключевого ресурса наблюдателя:
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"];
}];
}
Edit: оказывается, есть дополнительный случай: если выбран один ряд, и это флажок «unclicked,» это делает не автоматически запускает уведомление selectedObjects, поэтому вы должны запустить функцию на таймере для реализации нового выбора.