所以我完全被这一个难倒,并试图称之为“操作系统错误”。
我有一个带有单个部分的TableView控制器,在该部分的标题中有一个UITextField。多个操作导致添加/删除行没有问题。但是,只要在标题中编辑了文本,并且键盘被解除,任何行的插入/删除都会立即导致崩溃。
它实际上可以进一步简化 - 一旦键盘被解除,只需在桌面上调用beginUpdates / endUpdates就足以导致崩溃。 callstack的结尾是:
_CFTypeCollectionRetain
_CFBasicHashAddValue
CFDictionarySetValue
-[UITableView(_UITableViewPrivate) _updateWithItems:withOldRowData:oldRowRange:newRowRange:context:]
-[UITableView(_UITableViewPrivate) _endCellAnimationsWithContext:]
-[UITableView endUpdates]
我已经汇总了一个展示问题的最小例子。
完整的控制器来源:http://www.andrewgrant.org/public/TableViewFail.txt
示例项目:http://www.andrewgrant.org/public/TableViewCrash.zip
最相关的代码:
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
// create header view
UIView* header = [[[UIView alloc] initWithFrame:CGRectMake(0.f, 0.f, 320.f, 50.f)] autorelease];
// text field
UITextField* textField = [[[UITextField alloc] initWithFrame:CGRectMake(10.f, 12.f, 300.f, 28.f)] autorelease];
textField.text = @"Edit, then 'Save' will crash";
textField.borderStyle = UITextBorderStyleRoundedRect;
textField.clearButtonMode = UITextFieldViewModeAlways;
textField.delegate = self;
[header addSubview:textField];
return header;
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
// no purpose, but demonstrates updates work at this point
[self.tableView beginUpdates];
[self.tableView endUpdates];
[textField resignFirstResponder];
// immediate crash
[self.tableView beginUpdates];
[self.tableView endUpdates];
return YES;
}
答案 0 :(得分:1)
只是一个更新 - 我向Apple提交了一个错误报告并重新发布了案例,他们确认这是iOS 4.0中的一个错误。从iOS 4.1 beta 2开始,它还没有修复。
我的工作是将表格的第一行转换为占据整个内容视图并具有自定义高度的伪标题。它不是那么好(例如,事物无法到达屏幕的边缘),但它很接近并且不会崩溃。
答案 1 :(得分:1)
我昨晚遇到了这个漏洞,今天早上花了几个小时试图搞清楚。这个帖子中的其他答案对我不起作用,但确实帮助我想出了一个我认为最好的解决方法。
Cameron建议将屏幕外UITextField作为firstResponder,然后在tableview上调用endUpdates之前重新签名。这对我不起作用,但它给了我一个想法。
在我的自定义标题视图的上下文中,我在调用resignFirstResponder之前重新父文本字段(在我的例子中,实际上是UISearchBar)。然后我把它放回去了:
[self.window addSubview: sb];
[sb resignFirstResponder];
[self addSubview: sb];
几行后,当我调用[tableView endUpdates]时,它不再崩溃。
编辑:它有点复杂了。问题是如果第一响应者状态被撤销(例如,用户解除键盘),则不会执行此父交换代码,并且我们最终会崩溃。我目前的解决办法是在UITextField上放置一个类别覆盖resignFirstResponder - 似乎有效但不确定是否有任何不良副作用。
@implementation UITextField (private)
- (BOOL) resignFirstResponder
{
UIView* superviewSave = self.superview;
[self.window addSubview: self];
BOOL success = [super resignFirstResponder];
[superviewSave addSubview: self];
return success;
}
@end
答案 2 :(得分:0)
我自己遇到了这个错误,我很高兴找到了你的帖子,因为我正在我的桌子上敲我的头试图找出我搞砸的地方。
对于我的解决方法,我在进行更新之前创建了一个屏幕外UITextField
并在该文本字段上调用becomeFirstResponder
然后调用resignFirstResponder
。这避免了崩溃,并且不需要重新设计标题或单元格。
答案 3 :(得分:0)
将汤姆的解决方案更进一步,我注意到这个解决方案仅适用于iOS 4.X,这是可以的,因为此问题仅存在于iOS 4.X中。因此我将他的方法改为:
@implementation customUITextField
- (BOOL)resignFirstResponder {
if ( [[UIDevice currentDevice].systemVersion characterAtIndex:0] == '4' ) {
UIView* superviewSave = self.superview;
[self.window addSubview:self];
BOOL success = [super resignFirstResponder];
[superviewSave addSubview:self];
return success;
}
return [super resignFirstResponder];
}
@end