首页 > 解决方案 > 打开时停止键盘隐藏 UITextField

问题描述

寻找一种解决方案,以阻止 UITextField 在打开时被键盘隐藏,无论 UITextField 在视图层次结构中的位置以及其他 UI 元素如何。

标签: iosxamarinkeyboarduitextfield

解决方案


在线有很多解决方案可以调整您的视图,以便 UITextField 在出现时不会隐藏在键盘后面,但是,我找不到“一刀切”的解决方案,因此不得不调整很多答案和做我自己的。这是我想出的解决方案。希望它可以帮助某人:)

首先,此解决方案假定您的“主要父级”或“顶级”视图是 UIScrollView 或后代(例如 UITableView 或 UICollectionView)。如果您的层次结构中没有 UIScrollView,这将不起作用,因为这是用于将 UITextField 滚动到位的内容。其他解决方案向您展示了如何滚动任何 UIView,但是当您考虑它时,如果您需要将 UITextField 滚动到位,那么您的视图可能无论如何都应该是可滚动的,以阻止它离开屏幕。

在下面创建扩展方法。这将搜索与给定查询匹配的子视图(等)的子视图,并将它们作为数组返回。我们稍后会使用它。

public static UIView[] Find(this UIView view, Func<UIView, bool> query)
{
    if (view == null)
        return null;

    var views = new List<UIView>();

    if (query.Invoke(view))
        views.Add(view);

    foreach (var subview in view.Subviews)
    {
        var foundViews = subview.Find(query);

        if (foundViews != null)
            views.AddRange(foundViews);
    }

    return views.ToArray();
}

将以下方法添加到您的 UIViewController。这会从视图层次结构中找到顶级 UIScrollView。应该只有一个结果。

private UIScrollView FindAdjustmentScrollView()
{
    var scrollViews = View.Find(v => v is UIScrollView && v.FindSuperviewOfType(View, typeof(UIScrollView)) == null);

    return scrollViews.Length > 0 ? scrollViews[0] as UIScrollView : null;
}

添加以下事件处理程序。接下来我们将向观察者注册这些。

private void Keyboard_Appear(NSNotification notification)
{
    var firstResponder = View.Find(v => v.IsFirstResponder).FirstOrDefault();

    var scrollView = FindAdjustmentScrollView();

    if (firstResponder == null || scrollView == null || !(notification.UserInfo[UIKeyboard.FrameEndUserInfoKey] is NSValue value))
        return;

    var keyboardBounds = value.CGRectValue;
    var firstResponderAbsoluteFrame = firstResponder.Superview.ConvertRectToView(firstResponder.Frame, View);

    // This is how much of a gap you would like there to be between the bottom of the UITextField
    // and the top of the keyboard. Not mandatory but a nice touch in my experience.
    var offset = 8;

    var bottom = firstResponderAbsoluteFrame.Y + firstResponderAbsoluteFrame.Height + offset;
    var scrollAmount = keyboardBounds.Height - (scrollView.Frame.Size.Height - bottom);

    if (scrollAmount > 0)
        scrollView.SetContentOffset(0, scrollView.ContentOffset.Y + scrollAmount);
}

private void Keyboard_Disappear(NSNotification notification)
{
    var firstResponder = View.Find(v => v.IsFirstResponder).FirstOrDefault();

    var scrollView = FindAdjustmentScrollView();

    if (firstResponder == null || scrollView == null)
        return;

    scrollView.SetContentOffset(0, 0);
}

将这 2 个字段添加到您的 UIViewController。通过保持对观察者的对象引用,我们可以稍后在 UIViewController 不再可见时取消注册它们。

private NSObject _keyboardWillShowObserver, _keyboardWillHideObserver;

在 ViewWillAppear(或您注册事件处理程序的任何地方)中添加这两行。这些注册观察者并保留对象引用,以便我们以后可以取消注册。

_keyboardWillShowObserver = NSNotificationCenter.DefaultCenter.AddObserver(UIKeyboard.WillShowNotification, Keyboard_Appear);
_keyboardWillHideObserver = NSNotificationCenter.DefaultCenter.AddObserver(UIKeyboard.WillHideNotification, Keyboard_Disappear);

在 ViewWillDisappear 中(或在您取消注册事件处理程序的任何地方)添加这两行。这些从 UIViewController 注销观察者,因此它们不再响应事件。

NSNotificationCenter.DefaultCenter.RemoveObserver(_keyboardWillShowObserver);
NSNotificationCenter.DefaultCenter.RemoveObserver(_keyboardWillHideObserver);

澄清几点:

  • 当您的 UITextField 是向下许多层的子视图(甚至是 UITableViewCell 或 UICollectionViewCell)时,此解决方案有效。
  • 此解决方案确实考虑了对键盘视图所做的任何修改。例如:如果您在键盘顶部添加一个“完成”按钮,这将被计算出来。
  • 滚动调整是动画的。
  • 该解决方案确实适用于不同的方向。

推荐阅读