Как я могу сделать кнопку внутри NSTableViewRow реагировать на представленный объект

Я уже давно борюсь с этой проблемой. Я работаю над модулем диспетчера копирования файлов, и пока мне удалось заставить все работать идеально, за исключением кнопки отмены. По какой-то причине, когда я нажимаю кнопку отмены для определенной строки, действие кнопки предназначается для нескольких строк одновременно.

Диспетчер файлов

После нескольких дней исследования проблемы я смог заставить объект отменить операцию, представленную строкой, успешно используя:

-(IBAction)btnCancelOperationClick:(id)sender {
    NSInteger row = [_tableView rowForView:sender];
    if (row != -1) {
        FileCopyOperation *opr = [_fileCopyOperations objectAtIndex:row];
        [opr cancel];
        [_fileCopyOperations removeObjectAtIndex:row];
        [_tableView removeRowsAtIndexes:[NSIndexSet indexSetWithIndex:row] withAnimation:NSTableViewAnimationEffectFade];
    }
}

Это хорошо работает, я могу отменить операцию и соответственно обновить свою таблицу. Все остальное работает как задумано, но должно быть что-то не так с моим кодом или привязками. Я загружаю эту ячейку из кончика, затем я регистрирую этот кончик, используя:

[_tableView registerNib:[[NSNib alloc]initWithNibNamed:@"FileCopyCell" bundle:nil] forIdentifier:@"FileCopyCell"];

Я сделал QueueController владельцем файла и подключил все так:

Наручники

Я был бы очень признателен, если бы кто-то мог указать мне правильное направление, чтобы эта штука работала правильно Заранее спасибо!

Изменить, чтобы добавить больше образца кода.

-(NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
    FileCopyCell *cell = [tableView makeViewWithIdentifier:@"FileCopyCell" owner:self];
    FileCopyOperation *opr = [_fileCopyOperations objectAtIndex:row];

    [cell.fileName setStringValue:[NSString stringWithFormat:@"Copying \"%@\"",opr.fName]];
    [cell.progressBar setDoubleValue:((opr.bWritten.doubleValue / opr.fSize.doubleValue) * 100)];
    [cell.totalBytes setStringValue:[NSString stringWithFormat:@"of %@",[NSByteCountFormatter stringFromByteCount:opr.fSize.longLongValue countStyle:NSByteCountFormatterCountStyleFile]]];
    [cell.status setStringValue:[NSString stringWithFormat:@"%@",[NSByteCountFormatter stringFromByteCount:opr.bWritten.longLongValue countStyle:NSByteCountFormatterCountStyleFile]]];
    [cell.icon setImage:[[NSWorkspace sharedWorkspace]iconForFile:opr.srcURL.path]];
    [cell.cancelButton setTarget:self];
    return cell;
}

-(void)windowDidLoad {
    [super windowDidLoad];
    _fileCopyOperations = [NSMutableArray new];
    windowFrame = [self.window frame];
    rows = 0;

    [_tableView registerNib:[[NSNib alloc]initWithNibNamed:@"FileCopyCell" bundle:nil] forIdentifier:@"FileCopyCell"];

    if (!fileCopyManager) {
        fileCopyManager = [FileCopyManager sharedFileCopyManager];
        [fileCopyManager.fileCopyQueue addObserver:self forKeyPath:@"operationCount" options:NSKeyValueObservingOptionNew context:(void*)fileCopyManager];
    }

    [_scrollView setHasHorizontalScroller:NO];
    [_scrollView setHasVerticalScroller:NO];
}

2 ответа

Решение

Лучший подход состоит в том, чтобы подкласс NSTableCellView и пусть он обрабатывает свои собственные действия и представленный объект. Например, ячейка, представляющая Foo Экземпляр может иметь два свойства: foo а также fooController, Когда (nonatomic) foo сеттер вызывается, ячейка может обновить свой собственный интерфейс для представления переданного Foo, Когда Foo Контроллер создает ячейку таблицы, он может создавать FooCell экземпляр, установите себя как fooController и назначить Foo экземпляр и пусть клетка обрабатывает сама. Целью кнопки отмены может быть ее собственная ячейка, и когда -cancel: действие называется, оно может сказать fooController что делать (так как Foo контроллер отвечает за обновление очереди и табличного представления) и так как он имеет ссылку на его foo, это может передать это контроллеру через некоторые -cancelFoo:(Foo *)theFoo метод, не полагающийся на контроллер для поиска его индекса (который может быть неточным, если вы анимируете появление и исчезновение строк или пользователь быстро отменяет связку в ряду, но их удаление задерживается и обновляется асинхронно).

Красиво и чисто. Аккуратное и организованное сдерживание / разделение обязанностей. Ячейка обрабатывает свое собственное обновление пользовательского интерфейса и действия и знает свой собственный foo; Контроллер foo управляет своей коллекцией foo, ее табличным представлением и назначением foos для ячеек foo.

Благодаря Джошуа Ноцци, следуя его рекомендации, я перенес действие кнопки с контроллера на класс ячеек. В классе я использовал нижеприведенный метод для доступа к представленному объекту и отправки [operation cancel] сообщение.

-(IBAction)cancelOpr:(id)sender {
    NSButton *button = (NSButton*)sender;
    FileCopyOperation *opr = [(NSTableCellView*)[button superview]objectValue];
    [opr cancel];
    // This code calls the controller [removeObject:] method that takes care of cleaning everything out and updates the GUI accordingly.
    AppDelegate *ad = (AppDelegate*)[[NSApplication sharedApplication]delegate];
    QueueWindowController *qc = [ad getQueueController];
    [qc.fileCopyOperations performSelectorOnMainThread:@selector(removeObject:) withObject:opr waitUntilDone:YES];
}

Вы можете получить полный проект здесь.

Другие вопросы по тегам