如何测试键盘是不是覆盖 UITableView 中的 UITextField?

Posted

技术标签:

【中文标题】如何测试键盘是不是覆盖 UITableView 中的 UITextField?【英文标题】:How to test if keyboard is covering UITextField in UITableView?如何测试键盘是否覆盖 UITableView 中的 UITextField? 【发布时间】:2013-10-06 16:26:20 【问题描述】:

我如何检查键盘是否覆盖了UIScrollView 内的第一响应者(可能是UITableView,也可能不是UITableView)?请注意,UIScrollView 不一定会覆盖整个 viewController 的视图,可能会包含在模态视图中 (UIModalPresentationFormSheet)。

我正在使用来自Apple's reference documentation and example 的修改后的代码,但是即使键盘明显覆盖了第一响应者,CGRectContainsPoint 也会返回 false。很明显我没有正确使用convertRect:toView

另外,Apple 的代码没有考虑到视图不是全屏的,因此将 scrollView 的 contentInset 设置为键盘的全高并不是一个很好的解决方案——它应该只插入到覆盖 firstResponder 的键盘。

- (void)keyboardWasShown:(NSNotification*)aNotification


    // self.scrollView can be a tableView or not. need to handle both
    UIView *firstResponderView = [self.scrollView findFirstResponder];
    if (!firstResponderView)
        return;

    NSDictionary* info = [aNotification userInfo];
    CGRect rect = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];

    // convertRect:toView? convertRect:fromView? Always confusing
    CGRect kbRect = [self.scrollView convertRect:rect toView:nil];
    CGRect viewRect = [self.scrollView convertRect:firstResponderView.bounds toView:nil];

    // doesn't work. convertRect misuse is certainly to blame 
    if (!CGRectContainsPoint(kbRect, firstResponderView.frame.origin))
        return;

    // Only inset to the portion which the keyboard covers?
    UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbRect.size.height, 0.0);
    self.scrollView.contentInset = contentInsets;
    self.scrollView.scrollIndicatorInsets = contentInsets;

【问题讨论】:

附带说明:这也不是您可能想做的事情:[self.scrollView convertRect:firstResponderView.bounds toView:nil]firstResponderView.bounds 不在滚动视图坐标中,而是在 firstResponderView 的局部坐标系中。您可能想在此处使用框架(在 superview 的 CS 中。 【参考方案1】:

没有进一步测试或深入了解逻辑,这行似乎很奇怪:

CGRect kbRect = [self.scrollView convertRect:rect toView:nil];

键盘矩形(包含在通知中)位于 window 坐标中,您可能希望将其转换为滚动视图坐标系。 [viewA convertRect:rect toView:viewB] 将 rect 从 viewA 的坐标系转换为 viewB 的坐标系,因此您实际上所做的与您应该做的相反(正如您所怀疑的那样)。

我通常做的是这样的:

- (void)keyboardWillShow:(NSNotification *)aNotification

    NSDictionary *info = [aNotification userInfo];
    CGRect kbRect = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
    kbRect = [self.view.window convertRect:kbRect toView:self.view];    // convert to local coordinate system, otherwise it is in window coordinates and does not consider interface orientation
    _keyboardSize = kbRect.size;    // store for later use

    [UIView animateWithDuration:0.25 animations:^
        UIEdgeInsets insets = UIEdgeInsetsMake(0.0f, 0.0f, MAX(0.0f, CGRectGetMaxY(_tableView.frame) - CGRectGetMinY(kbRect)), 0.0f);    // NB: _tableView is a direct subview of self.view, thus _tableView.frame and kbRect are in the same coordinate system
        _tableView.contentInset = insets;
        _tableView.scrollIndicatorInsets = insets;
        [self scrollToActiveTextField];    // here I adapt the content offset to ensure that the active text field is fully visible
    ];

【讨论】:

刚刚注意到我的方法实际上完全解决了您的问题(“注意 UIScrollView 不一定会覆盖整个 viewController 的视图,并且可能包含在模态视图中”),因为我不只是盲目地使用键盘的高度以适应内容插入,但实际上考虑了滚动视图的框架,并且只适应键盘实际覆盖的增量。 我应该注意到self.view 不可用。键盘闪避由滚动视图管理器类处理,该管理器类通过滚动视图在初始化时进行管理,然后保存在弱引用中。用self.scrollView 替换self.view 有什么问题吗? 好吧,滚动视图坐标系实际上是受滚动影响的,所以可能不是一个好主意.. ;) 使用self.scrollView.superview怎么样? 是的,只要它不是层次结构中的根视图,它就可以工作。 我还要注意,动画持续时间应该从信息字典中提取,而不是硬编码。

以上是关于如何测试键盘是不是覆盖 UITableView 中的 UITextField?的主要内容,如果未能解决你的问题,请参考以下文章

使用 UISearchController 显示搜索结果和键盘覆盖 UITableView 中的底部行/部分

计算从键盘高度 UITableview 向上滚动多少

UITableView 键盘覆盖UITableViewCell的输入框解决方法(swift)

如何防止键盘覆盖我的 UI 而不是调整它的大小?

点击 UITextField 在 UITableView 中显示数据

如何让 UITableView 方法在另一个覆盖方法之前运行?