Как получить UITableViewCell из одного из его подзонов

У меня есть UITableView с UITextField в каждом из UITableViewCells. У меня есть метод в моем ViewController, который обрабатывает событие "Did End On Exit" для текстового поля каждой ячейки, и то, что я хочу сделать, - это обновить мои данные модели новым текстом.

В настоящее время у меня есть:

- (IBAction)itemFinishedEditing:(id)sender {
    [sender resignFirstResponder];

    UITextField *field = sender;
    UITableViewCell *cell = (UITableViewCell *) field.superview.superview.superview;

    NSIndexPath *indexPath = [_tableView indexPathForCell:cell];

    _list.items[indexPath.row] = field.text;
}

Конечно, работает field.superview.superview.superview, но это просто кажется таким взломанным. Есть ли более элегантный способ? Если я установил тег UITextField в indexPath.row ячейки, в которой он находится в cellForRowAtIndexPath, этот тег всегда будет правильным даже после вставки и удаления строк?

Для тех, кто уделяет пристальное внимание, вы можете подумать, что у меня есть один .superview слишком много, а для iOS6 вы были бы правы. Тем не менее, в iOS7 есть дополнительный вид (NDA предотвращает разработку формы) в иерархии между представлением содержимого ячейки и самой ячейкой. Это точно иллюстрирует, почему выполнение элемента супервизора немного хакерское, поскольку оно зависит от того, как реализовано UITableViewCell, и может ломаться с обновлениями ОС.

Ответ 1

Поскольку ваша цель - получить индексный путь для текстового поля, вы можете сделать это:

- (IBAction)itemFinishedEditing:(UITextField *)field {
    [field resignFirstResponder];

    CGPoint pointInTable = [field convertPoint:field.bounds.origin toView:_tableView];    

    NSIndexPath *indexPath = [_tableView indexPathForRowAtPoint:pointInTable];

    _list.items[indexPath.row] = field.text;
}

Ответ 2

Один из лучших способов сделать это - перебирать иерархию представлений, проверяя каждый супервизор, если он UITableViewCell, используя метод class. Таким образом, вы не ограничены количеством наблюдений между вашим UITextField и ячейкой.

Что-то по строкам:

UIView *view = field;
while (view && ![view isKindOfClass:[UITableViewCell class]]){ 
    view = view.superview;
}

Ответ 3

Вы можете прикрепить UITableViewCell себя как слабую связь с UITextField, а затем вырвать ее в методе UITextFieldDelegate.

const char kTableViewCellAssociatedObjectKey;

В подклассе UITableViewCell:

- (void)awakeFromNib {
    [super awakeFromNib];
    objc_setAssociatedObject(textField, &kTableViewCellAssociatedObjectKey, OBJC_ASSOCIATION_ASSIGN);
}

В вашем методе UITextFieldDelegate:

UITableViewCell *cell = objc_getAssociatedObject(textField, &kTableViewCellAssociatedObjectKey);
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
//...

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

Ответ 4

В основном в этом случае я бы предпочел, чтобы вы поместили метод IBAction в ячейку вместо контроллера. И затем, когда действие инициируется, ячейка отправляет делегат в экземпляр контроллера вида.

Вот пример:

@protocol MyCellDelegate;


@interface MyCell : UITableViewCell

@property (nonatomic, weak) id<MyCellDelegate> delegate;

@end

@protocol MyCellDelegate <NSObject>

- (void)tableViewCell:(MyCell *)cell textFieldDidFinishEditingWithText:(NSString *)text;

@end

В реализации ячейки:

- (IBAction)itemFinishedEditing:(UITextField *)sender
{
    // You may check respondToSelector first
    [self.delegate tableViewCell:self textFieldDidFinishEditingWithText:sender.text];
}

Итак, теперь клетка передаст себя и текст через метод делегата.

Предположим, что контроллер представления задал делегат ячейки для себя. Теперь контроллер представления реализует метод делегата.

В реализации вашего контроллера вида:

- (void)tableViewCell:(MyCell *)cell textFieldDidFinishEditingWithText:(NSString *)text
{
    NSIndexPath *indexPath = [_tableView indexPathForCell:cell];
    _list.items[indexPath.row] = text;
}

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