代码之家  ›  专栏  ›  技术社区  ›  Andrew Grant

UITableView文本编辑后更新崩溃

  •  4
  • Andrew Grant  · 技术社区  · 14 年前

    所以我完全被这一个难住了,我想称之为“操作系统错误”。

    我有一个带有单个部分的TableView控制器,并且在该部分的标题中有一个UItextfield。多个操作会导致行的添加/删除没有问题。但是,一旦在标题中编辑文本,并且键盘关闭,任何行的插入/删除都会立即导致崩溃。

    实际上,它可以进一步简化——只需在键盘关闭后调用表上的beginupdates/endupdates就足以导致崩溃。调用堆栈的结尾是:

    _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;
    }
    
    4 回复  |  直到 13 年前
        1
  •  1
  •   Andrew Grant    14 年前

    只是一个更新-我向苹果提交了一个bug报告和repro案例,他们确认这是iOS4.0中的一个bug。从iOS 4.1测试版2开始,它还没有被修复。

    我的工作是将表的第一行转换为一个伪标题,它占据整个内容视图并具有自定义的高度。虽然不是很好(比如说,屏幕边缘的东西都够不到),但它很近,不会崩溃。

        2
  •  1
  •   TomSwift    14 年前

    我昨晚打了这个虫子,今天早上花了几个小时想弄清楚。这条线索中的其他答案对我不起作用,但确实帮助我想出了一个我认为最好的解决方法。

    卡梅伦建议让屏幕外的uitextfield成为第一响应者,然后在调用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
    
        3
  •  0
  •   spybart    13 年前

    进一步考虑到Tom的解决方案,我注意到这个解决方案只在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
    
        4
  •  0
  •   DShah    13 年前

    我自己也遇到过这个问题,我很高兴找到你的帖子,因为我在办公桌上敲脑袋,想弄清楚我把事情搞砸了。

    为了解决问题,我在屏幕外创建了一个 UITextField 并称之为 becomeFirstResponder 然后 resignFirstResponder 在进行更新之前在该文本字段上。这避免了崩溃,并且不需要重新设计头部或单元。