UISwitch внутри пользовательского UITableViewCell недоступен

У меня есть UISwitch внутри пользовательского UITableViewCell (подкласс которого я называю RootLabeledSwitchTableCell).

Ячейка содержит UILabel и UISwitch рядом друг с другом.

У меня есть @property с именем keychainSwitch, который указывает на переключатель внутри этой пользовательской ячейки:

@interface RootLabeledSwitchTableCell : UITableViewCell {
    IBOutlet UILabel *textLabel;
    IBOutlet UISwitch *labeledSwitch;
}

@property (nonatomic, retain) IBOutlet UILabel *textLabel;
@property (nonatomic, retain) IBOutlet UISwitch *labeledSwitch;

@end

В моем делегате табличного представления я добавил селектор, который вызывается, если состояние переключателя перевернуто:

- (UITableViewCell *) tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath {
   NSString *CellIdentifier = [NSString stringWithFormat: @"%d:%d", [indexPath indexAtPosition:0], [indexPath indexAtPosition:1]];
   UITableViewCell *cell = [tv dequeueReusableCellWithIdentifier:CellIdentifier];
   if (cell == nil) {
       switch (indexPath.section) {
       case(kMyFirstSection): {
           switch (indexPath.row) {
               case(kMyFirstSectionFirstRow) {
                   [cellOwner loadMyNibFile:@"RootLabeledSwitchTableCell"];
                   cell = (RootLabeledSwitchTableCell *)cellOwner.cell;
                   self.keychainSwitch = [(RootLabeledSwitchTableCell *)cell labeledSwitch];
                   [self.keychainSwitch addTarget:self action:@selector(keychainOptionSwitched) forControlEvents:UIControlEventValueChanged];
                   break;
               }
               // ...
           }
       }
       // ...
   }
}

Итак, этот селектор работает правильно:

- (void) keychainOptionSwitched {
   NSLog(@"Switched keychain option from %d to %d", ![self.keychainSwitch isOn], [self.keychainSwitch isOn]);
}

Однако я не могу использовать метод -setOn:animated: экземпляра UISwitch для инициализации его начального состояния:

- (void) updateInterfaceState {
   BOOL isFirstTimeRun = [[[NSUserDefaults standardUserDefaults] objectForKey:kIsFirstTimeRunKey] boolValue];
   if (isFirstTimeRun) {
      [self.keychainSwitch setOn:NO animated:NO];
   }
}

По результатам тестирования self.keychainSwitch равно nil, из-за чего -setOn:animated ничего не делает, но я все еще могу управлять переключателем, и оператор NSLog будет правильно печатать изменения состояния переключателя, например:

[Session started at 2009-08-24 07:04:56 -0700.]
2009-08-24 07:04:58.489 MyApp[29868:20b] keychain switch is: nil
2009-08-24 07:05:00.641 MyApp[29868:20b] Switched keychain option from 1 to 0
2009-08-24 07:05:01.536 MyApp[29868:20b] Switched keychain option from 0 to 1
2009-08-24 07:05:02.928 MyApp[29868:20b] Switched keychain option from 1 to 0

Есть ли что-то, что мне не хватает в настройке self.keychainSwitch в методе делегата UITableView?


person Alex Reynolds    schedule 24.08.2009    source источник


Ответы (3)


Я сделал это некоторое время назад. Вы должны изменить свой селектор

[self.keychainSwitch addTarget:self action:@selector(keychainOptionSwitched:) forControlEvents:UIControlEventValueChanged];

А затем обновите свой метод до

-(void) keychainOptionSwitched:(id)sender {
UISwitch *tempSwitch = (UISwitch *)sender;
}

Теперь tempSwitch — это переключатель, который был изменен.

person Community    schedule 28.08.2009
comment
Это не сработало с моей стороны. Но спасибо за предложение. - person Alex Reynolds; 05.09.2009
comment
Здесь тоже не работал. Я получил: [OptionsViewController toggleImage]: нераспознанный селектор отправлен в экземпляр 0x4b305c0 '. Обратите внимание, что вызов без параметра работал нормально (хотя я, очевидно, не мог знать, какой переключатель его вызывает!) - person Julian; 22.12.2010
comment
Упс, заработало. При добавлении целевого действия убедитесь, что двоеточие включено в вызов селектора! Это работает @selector(keychainOptionSwitched:), это не @selector(keychainOptionSwitched). - person Julian; 22.12.2010

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

Вы делаете это следующим образом (в методе -tableView:cellForRowAtIndexPath):

UISwitch *mySwitch = [[[UISwitch alloc] initWithFrame:CGRectZero] autorelease];
cell.accessoryView = mySwitch;

Бит accessorView также отвечает за размер и позиционирование, поэтому мы можем обойтись без CGRectZero.

Затем вы можете использовать событие управления системой и метод setOn следующим образом (обратите внимание, что мы приводим его как указатель UISwitch, который мы знаем):

[(UISwitch *)cell.accessoryView setOn:YES];   // Or NO, obviously!
[(UISwitch *)cell.accessoryView addTarget:self action:@selector(mySelector)
     forControlEvents:UIControlEventValueChanged];

Надеюсь, это поможет!

person h4xxr    schedule 24.08.2009
comment
Да, я знаю о дополнительном виде. Я хотел бы использовать пользовательскую ячейку в этом конкретном случае, но спасибо! - person Alex Reynolds; 24.08.2009
comment
[ячейка addSubview:mySwitch]; не нужно. cell.accessoryView сохраняет mySwitch. - person Carlton Gibson; 14.06.2010
comment
Я проголосовал за вас, но простое проголосование не показалось вам достаточной благодарностью. Спасибо! Иногда я настолько сосредотачиваюсь на решаемой проблеме, что забываю об этих небольших дополнениях в фреймворке. - person Jason Whitehorn; 25.02.2011
comment
какой подход вы бы использовали, учитывая несколько таких ячеек таблицы, чтобы привязать отдельные коммутаторы к нужной цели? - person Alnitak; 28.01.2016

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

 self.keychainSwitch = [(RootLabeledSwitchTableCell *)cell labeledSwitch];

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

person Daniel    schedule 24.08.2009
comment
self.keychainSwitch = [[(RootLabeledSwitchTableCell *)cell labeledSwitch] сохранить]; не работает. - person Alex Reynolds; 24.08.2009
comment
к тому же у вас, вероятно, в любом случае сохраняется свойство ... что происходит, когда вы пытаетесь выполнить указанную выше строку без оглядки? - person Daniel; 24.08.2009
comment
Кроме того, при вызове метода -updateInterfaceState отображаются все ячейки табличного представления, включая ячейку с экземпляром keychainSwitch. - person Alex Reynolds; 24.08.2009
comment
Я указываю (неатомарное, сохранение) в определении свойства. Приложение не падает в любом случае: self.keychainSwitch просто ноль. - person Alex Reynolds; 24.08.2009
comment
проблема, которую я думаю, по-прежнему заключается в удалении ячеек из очереди, довольно сложно найти хорошее решение, не глядя на дополнительный код, но, возможно, попробуйте сохранить свои переключатели (или значения переключателей) в массиве в вашем viewController и когда вы запрашиваете ячейку для перезагрузки, вы можете получить ее значение оттуда (или установить ее вид переключения). Поскольку вы используете только один идентификатор удаления из очереди, ваши ячейки не сохранят состояние переключателя, поэтому вам все равно придется сбрасывать его каждый раз, когда вы перезагружаете ячейки (если это поведение, которое вы хотите) - person Daniel; 24.08.2009
comment
Думаю, я не уверен, что понимаю ваш ответ, так как я использую другой идентификатор удаления из очереди для каждого раздела и строки внутри раздела. - person Alex Reynolds; 24.08.2009
comment
как показано здесь UITableViewCell *cell = [tv dequeueReusableCellWithIdentifier:CellIdentifier]; идентификатор ячейки постоянный или вы его постоянно меняете? - person Daniel; 24.08.2009
comment
CellIdentifier определяется здесь: NSString *CellIdentifier = [NSString stringWithFormat: @%d:%d, [indexPath indexAtPosition:0], [indexPath indexAtPosition:1]]; - person Alex Reynolds; 24.08.2009
comment
хорошо, они уникальны, теперь позвольте мне предложить вам сделать это (учитывая, что вы знаете индексный путь строки, которую вы пытаетесь отключить), попробуйте RootLabeledSwitchTableCell *cell = [tv dequeueReusableCellWithIdentifier:CellIdentifier]; а потом выключи прямо на камере... - person Daniel; 24.08.2009
comment
Это приводит к предупреждениям компиляции, поскольку это неправильный тип, возвращаемый методом -dequeueReusableCellWithIdentifier:. - person Alex Reynolds; 24.08.2009
comment
примените его (RootLabeledSwitchTableCell*), убедитесь, что при этом ячейка, к которой вы обращаетесь, имеет этот тип (вы всегда можете задать тип, например) [cell isKindOfClass:[RootLabeleCell class] - person Daniel; 24.08.2009