背景
我有一个名为 _tableView 的带有 UITableView ivar 的类。该类实现了 UITableViewDatasource 和 UITableViewDelegate _tableView。UITableView 单元格是 FormTableCell 的实例。FormTableCell 是 UITableViewCell 的子类,具有 UILabel 和 UITextField。在课堂上,我有一个名为 textFieldDelegate 的属性。当 tableView:cellForIndexPath: 被调用时,当前的 textFieldDelegate 被分配给返回单元格的 textField 属性。此外,当类 textFieldDelegate 被修改时,我想更新单元格文本字段的委托。我想出了三种方法来做到这一点,但不知道最好的方法。
- 使用新的 textFieldDelegate 更新每个可见单元格的委托。
- 遍历所有单元格并分配新的 textFieldDelegate
- 重新加载 tableViews 数据。
选项 1 很好,但它假定 tableView:cellForIndexPath 将在显示不在 tableView 的 visibleCells 数组中的任何单元格之前被调用。如果这没有发生,那么不同的单元格可能有不同的代表,这不是我想要的。
选项 2 是最差的,因为它只适用于少量单元格,我希望 FormTables 总是有少量单元格,但我永远不知道。
如果我假设 tableView:cellForIndexPath: 在显示不可见单元格之前将被调用是无效的并且它胜过选项 2,因为它处理具有大量单元格的表格,则选项 3 胜过选项 1。
问题
哪个是最好的选择,我假设 tableView:cellForIndexPath: 将在不可见的单元格变得可见或与不正确的交互之前被调用?
代码
- (void)setTextFieldDelegate:(id<UITextFieldDelegate>)textFieldDelegate
{
if(_textFieldDelegate != textFieldDelegate) {
_textFieldDelegate = textFieldDelegate;
//Update _textFieldDelegate for all visible cells.
//!DEV: Assuming non-visible cells will be recreated with tableView:cellForIndexPath:
for (FormTableCell *cell in [_tableView visibleCells]) {
[[cell textField] setDelegate:_textFieldDelegate];
}
//!DEV: This will potentially generate memory issues for a large number of cells
for(NSUInteger i = 0; i < kNumberOfFields; ++i) {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:(NSInteger)i inSection:0];
FormTableCell *cell = (FormTableCell*)[_tableView cellForRowAtIndexPath:indexPath];
[[cell textField] setDelegate:_textFieldDelegate];
}
//!DEV: Maybe this is the best method, but it requires the recreation of visibleCells.
[_tableView reloadData];
}
}
最终状态
所以这就是我最终做的。我实现了一个装饰委托(?)
- (void)setTextFieldDelegate:(id<UITextFieldDelegate>)textFieldDelegate
{
if(_textFieldDelegate != textFieldDelegate) {
_textFieldDelegate = textFieldDelegate;
}
}
#pragma mark - UITextFieldDelegate Implementation
//This class forwards UITextFieldDelegate methods on to _textFieldDelegate, if implemented.
//Provides default functionality is not implemented.
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
if([_textFieldDelegate respondsToSelector:@selector(textFieldShouldBeginEditing:)]) {
return [_textFieldDelegate textFieldShouldBeginEditing:textField];
} //implicit else
return YES;
}
- (void)textFieldDidBeginEditing:(UITextField *)textField {
//Setup _closeKeyboardButton behind _tableView
//view::closeKeyboardButton (_closeKeyboardButton)
NSAssert(_closeKeyboardButton == nil, @"!DEV: Thought _closeKeyboardButton would always be nil here.");
if(_closeKeyboardButton != nil) {
[_closeKeyboardButton removeFromSuperview];
_closeKeyboardButton == nil;
}
CGRect closeKeyboardFrame = {CGPointZero, [self frame].size};
UIButton *closeKeyboardButton = [UIButton buttonWithType:UIButtonTypeCustom];
[closeKeyboardButton setFrame:closeKeyboardFrame];
[closeKeyboardButton addTarget:self
action:@selector(_closeKeyboardButtonTapped:)
forControlEvents:UIControlEventTouchUpInside];
[self insertSubview:closeKeyboardButton belowSubview:_tableView];
_closeKeyboardButton = closeKeyboardButton;
if([_textFieldDelegate respondsToSelector:@selector(textFieldDidBeginEditing:)]) {
[_textFieldDelegate textFieldDidBeginEditing:textField];
}
}
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField
{
if([_textFieldDelegate respondsToSelector:@selector(textFieldShouldEndEditing:)]) {
return [_textFieldDelegate textFieldShouldEndEditing:textField];
} //implicit else
return YES;
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
if([_textFieldDelegate respondsToSelector:@selector(textFieldDidBeginEditing:)]) {
[_textFieldDelegate textFieldDidBeginEditing:textField];
}
//Tear down _closeKeyboardButton
NSAssert(_closeKeyboardButton != nil, @"!DEV: Thought _closeKeyboardButton would always exist here.");
[_closeKeyboardButton removeFromSuperview];
_closeKeyboardButton = nil;
}
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if([_textFieldDelegate respondsToSelector:@selector(textField:shouldChangeCharactersInRange:replacementString:)]) {
return [_textFieldDelegate textField:textField shouldChangeCharactersInRange:range replacementString:string];
} //implicit else
return YES;
}
- (BOOL)textFieldShouldClear:(UITextField *)textField
{
if([_textFieldDelegate respondsToSelector:@selector(textFieldShouldClear:)]) {
return [_textFieldDelegate textFieldShouldClear:textField];
} //implicit else
return YES;
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
if([_textFieldDelegate respondsToSelector:@selector(textFieldShouldReturn:)]) {
return [_textFieldDelegate textFieldShouldReturn:textField];
} //implicit else
return NO;
}