如何从UITableViewCell的子视图获取它本身

13

我有一个带有每个 UITableViewCell 中的一个 UITextFieldUITableView。 我在我的视图控制器中编写了一个方法,用于处理每个单元格文本字段的“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 可以起作用,但这似乎有些不正规。有没有更优雅的方法?如果我在 cellForRowAtIndexPath 中将 UITextField 的标记设置为其所在单元格的 indexPath.row,那么即使插入和删除行,该标记是否始终正确?对于那些密切关注的人,你可能认为在那里我有一个多余的 .superview,对于 iOS6,你是正确的。但是,在 iOS7 中,存在一个额外的视图(NDA 防止我详细说明),位于单元格内容视图和单元格本身之间的层次结构中。这恰好说明了为什么执行 superview 的操作有点不正规,因为它依赖于了解如何实现 UITableViewCell,并且可能会随着操作系统的更新而破坏。
4个回答

31

既然你的目标是获取文本字段的索引路径,你可以这样做:

- (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;
}

不错!我非常喜欢这个。 - Jacob Relkin
1
请记住,仅在您的表行不能添加、删除或重新排序时,使用标签来索引路径才有效。 - rmaddy
1
如果您在屏幕上某些可见行的索引路径上方插入单个行,则插入点下方的可见行不会更新,并且仍将具有旧标记。这假定您没有调用reloadData,只是调用了insertRowsAtIndexPaths:。如果您只是将可见单元格从屏幕的一个部分重新排序到另一个可见屏幕的部分,以便没有单元格进入或离开屏幕,则根本不会调用cellForRowAtIndexPath:。同样,这假定您没有调用reloadData - rmaddy
1
@maddy 顺便说一下,在你的示例代码中,self.bounds.origin 应该是 field.bounds.origin。我试图编辑你的答案,但 SO 不允许我做出这样小的更改。 - mluisbrown
从 'viewDidLoad' 方法中获取单元格怎么样?有人能解释一下吗? - LKM
显示剩余4条评论

12

有一个稍微更好的方法是通过迭代查找视图层次结构,在每个父视图中检查是否为 UITableViewCell,使用 class 方法。这样,您就不会受到位于 UITextField 和单元格之间的父视图数量的限制。

可以采用以下类似的方式:

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

真的非常出色和精彩!非常感谢,你为我节省了很多时间。只是不要忘记将“view”转换为“UITableViewCell*”,它就像魔法一样运行,并且非常紧凑。(但是为什么我自己没有想到呢?;-)) - Chrysotribax

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中取消列队一个单元格时,重新关联以确保文本字段与正确的单元格相关联。

2
+1 因为我以前从未听说过 objc_setAssociatedObject()! - mluisbrown

1
基本上,在这种情况下,我更喜欢您将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];
}

现在一个单元格将通过代理方法传递自身和文本。

假设视图控制器已将单元格的代理设置为self。现在视图控制器将实现一个代理方法。

在您的视图控制器的实现中:

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

这种方法适用于无论苹果如何更改表视图单元格的视图层次结构。

1
我认为这可能违反了MVC原则。还有其他人这样认为吗? - Jacob Relkin
1
@JacobRelkin 我同意。这会把一个属于控制器的职责交给了单元格。 - Cezar
这种方法(为您的UITableViewCell创建自定义委托协议)正是Ray Wenderlich教程中建议的方法,用于制作“Clear”样式的待办事项列表应用程序(搜索SHCTableViewCellDelegate以进入正确的部分)。我不确定它是否会破坏MVC。单元格只是通知控制器发生了某些事情,由控制器根据需要进行操作(例如更改模型)。 - mluisbrown

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接