iOS6 和 UITableView 中的屏幕外 UITextView

Posted

技术标签:

【中文标题】iOS6 和 UITableView 中的屏幕外 UITextView【英文标题】:iOS6 and offscreen UITextView in UITableView 【发布时间】:2012-10-04 19:39:33 【问题描述】:

我有一个 UITableViewController,它在每个单元格中都有一个 UITextField。当用户在一个字段中点击返回时,它会自动将下一个字段设置为第一响应者并滚动表格以显示该字段,如下所示:

- (BOOL) textFieldShouldReturn:(UITextField *)textField 
  
    UITextField *nextTextField=nil;
    NSInteger row;
    NSInteger section;

    if(textField == self.txtFieldA) 
    
        nextTextField = self.txtFieldB;
        row=1; section=0;
    
    else if(textField == self.txtFieldB) 
    
        nextTextField = self.txtFieldC;
        row=2; section=0;
    
    else 
    
        [textField resignFirstResponder];
    

    if(nextTextField) 
            
        NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section];
        [nextTextField becomeFirstResponder];

        [self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];      
    

当您选择一个文本字段(例如 txtFieldA),向下滚动表格以使 txtFieldB 不可见,然后按 Enter 键时,问题就出现了。它在 ios5 上按预期工作(txtFieldB 成为新的第一响应者)。但是,在 iOS6 上却没有。它滚动到表格中的正确位置,但 txtFieldB 没有成为第一响应者(我不确定是什么——当我在导航控制器上点击“返回”时,键盘仍然可见)。

当我点击“返回”时,不会触发 txtFieldB 的 didBeginEditing。如果我将[nextTextField becomeFirstResponder] 延迟一秒钟左右(直到滚动动画完成,这使得 nextTextField 可见)它会按预期工作,但这样做似乎很笨拙且不太顺利,所以我宁愿了解出了什么问题而不是实现类似的东西.

【问题讨论】:

【参考方案1】:

在我对这个问题的解决方案中,我将 UITableViewCell 子类化为一个文本字段单元格。在那个文本字段单元格中,我覆盖了-setSelected:animated:

- (void)setSelected:(BOOL)selected animated:(BOOL)animated

    [super setSelected:selected animated:animated];

    if (selected) 
        [self.textField becomeFirstResponder];
    

回到我的视图控制器,当我想前进到下一个字段时,我只需选择它所在的行。

- (IBAction)advanceToNextTextField

    NSIndexPath *indexPath = [self nextIndexPathFromIndexPath:self.indexPathOfActiveField];

    [self.tableView selectRowAtIndexPath:indexPath
                                animated:YES scrollPosition:UITableViewScrollPositionNone];
    [self.tableView scrollToRowAtIndexPath:indexPath
                          atScrollPosition:UITableViewScrollPositionNone animated:YES];

这使得处理返回键变得容易。

- (BOOL)textFieldShouldReturn:(UITextField *)textField

    switch (textField.returnKeyType) 
        case UIReturnKeyNext: [self advanceToNextTextField]; break;
        case UIReturnKeyDone: [self resignFirstResponder]; break;
        default:              break;
    

    return YES;

希望对您有所帮助。

【讨论】:

这绝对是一种更好的设置方式,但不确定是否能解决下一个单元格离开屏幕时点击“下一个”的问题。在 iOS6 上编译这段代码是否有效? 是的,它适用于 iOS6。此代码直接从我的应用程序中提取,该应用程序旨在在 iOS5 和 iOS6 中运行。【参考方案2】:

我还有一个在 iOS 6 之前运行良好的现有项目。我的通用应用程序使用 UITableView 在键盘工具栏上创建一个带有“下一个”和“上一个”按钮的表单(类似于 Safari)。该表显示了包含文本字段的UITableViewCell 子类的实例。下一个/上一个功能也包含 - 所以最后一个字段的“下一个”会将焦点返回到表单的顶部。

很遗憾,Jeffrey Thomas 的解决方案并不适合我。如果一个单元格不在屏幕上,则提前选择和滚动方法ToNextField 方法适用于单元格本身。在setSelected 方法中,文本字段的视图层次结构是正确的:UITextField -> UITableViewCellContentView -> UITableViewCell 子类 -> UITableView

但是,becomeFirstResponder 调用仍然无法激活字段的编辑模式、触发textFieldDidBeginEditing 等。键盘仍然可见,但不会影响现在可见的单元格。

目前,我能够绕过此问题的唯一方法是先滚动而不使用动画

[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionNone animated:NO];
[self.tableView selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionNone];

就像 Ned 在操作中的延迟技术一样,这可以确保在应用要求它响应时文本字段是可见的。否则,似乎becomeFirstResponder 在动画期间和文本字段(完全)可见之前正在触发。显然,iOS 6 中的一些变化对第一响应者消息和可见性更加严格!

遗憾的是,当从一个场地移动到另一个场地时,它失去了流畅的动画效果。假设这不仅仅是 iOS 6.0 中的错误,我计划在未来不再使用 UITableView 作为表单。

【讨论】:

【参考方案3】:

在我的同事 Charles 的帮助下找到了解决这个问题的方法(谢谢!)。我还尝试了 Jeffrey Thomas 的解决方案(及其变体),但没有成功:如果其表格单元格不在屏幕上,则文本字段不会成为第一响应者,正如 Ogel 所描述的那样。我寻找了UITableViewDelegate 方法,当tableview 完成动画并延迟调用文本字段的setSelected: 方法时会提醒我,直到那时,没有运气。然后他指给我看

- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView

当它完成滚动以响应表格视图方法时,它也会被表格视图触发(UITableViewDelegate 符合 UIScrollViewDelegate

- (void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated

所以关键是计算您打算在下一个/上一个方法中选择的行的索引路径(就像在 Jeffrey Thomas 的 - (IBAction)advanceToNextTextField 方法中一样),但不要选择它,只需滚动到它,然后设置ivar 为它:

[_table scrollToRowAtIndexPath:nextIndexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
_selectIndexPathOnAnimationEnd = nextIndexPath;

然后在滚动视图委托方法中,在表格视图完成滚动到该索引路径后选择该行:

- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView 
    if(_selectIndexPathOnAnimationEnd != nil) 
        [_table selectRowAtIndexPath:_selectIndexPathOnAnimationEnd animated:NO scrollPosition:UITableViewScrollPositionMiddle];
        _selectIndexPathOnAnimationEnd = nil;
    

此时文本字段可以成为第一响应者。

【讨论】:

以上是关于iOS6 和 UITableView 中的屏幕外 UITextView的主要内容,如果未能解决你的问题,请参考以下文章

iOS6与iOS7屏幕适配技巧

打印屏幕外有内容的 UITableView?

即使在屏幕外,iOS UITableView 单元格也会加载

UITableView 没有正确地为在屏幕外重新排序的单元格设置动画

屏幕外(不可见)TableViewCells 不设置动画

表视图边界外的 UITableViewCell