iPhone X键盘显示出额外的空间

时间:2017-11-14 12:43:35

标签: ios swift iphone-x

我创建了一个聊天用户界面,我在其中为constraint添加了tableView到屏幕底部。我正在通过添加键盘的高度来更改约束值,该键盘在除iPhone X之外的所有设备中都能正常工作。

当键盘不可见时的UI:

enter image description here

哪个好。

问题是当键盘出现时,textView和键盘之间显示空格:

enter image description here

我是否必须为此尝试不同的方法,或者可以使用约束来解决它?

6 个答案:

答案 0 :(得分:35)

在计算约束的值时,尝试减去安全区域底部插入的高度。

以下是处理UIKeyboardWillChangeFrame通知的示例实现。

@objc private func keyboardWillChange(_ notification: Notification) {
    guard let userInfo = (notification as Notification).userInfo, let value = userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue else { return }
    let newHeight: CGFloat
    if #available(iOS 11.0, *) {
        newHeight = value.cgRectValue.height - view.safeAreaInsets.bottom
    } else {
        newHeight = value.cgRectValue.height
    }
    myConstraint.value = newHeight
}

答案 1 :(得分:0)

ViewController的约束出口。我现在将它称为yourConstraint。然后添加代码以发现键盘何时显示以及何时被解除键盘。在那里,您可以相应地更改约束的constant。这允许您继续使用约束。

viewWillAppear

NotificationCenter.default.addObserver(self, selector: #selector(YourViewController.keyboardWillShow), name:        NSNotification.Name.UIKeyboardWillShow, object: nil) // <=== Replace YourViewController with name of your view controller
NotificationCenter.default.addObserver(self, selector: #selector(YourViewController.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil) // <=== Replace YourViewController with name of your view controller

viewWillDisappear

NotificationCenter.default.removeObserver(self)

UIViewController

@objc private func keyboardWillShow(notification: Notification) {
    guard let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue else {
        yourConstraint.isActive = false // <=== 
        view.layoutIfNeeded()
        return
    }
    let duration: TimeInterval = ((notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue) ?? 0.4
    yourConstraint.constant = newValue // <===
    UIView.animate(withDuration: duration) {
        self.view.layoutIfNeeded()
    }
}

@objc private func keyboardWillHide(notification: Notification) {
    let duration: TimeInterval = ((notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue) ?? 0.4
    yourConstraint.constant = oldValue
    UIView.animate(withDuration: duration) {
        self.view.layoutIfNeeded()
    }
}

UIView.animate不是必需的(块的内部是),但它使转换看起来很好。

答案 2 :(得分:0)

在iPhone X中,UIKeyboardFrame 开始 UserInfoKey报告的键盘值在这两种情况下有所不同:

  • 键盘不可见,文本字段成为第一响应者
  • 键盘已经可见,另一个文本字段成为第一响应者。

要获得键盘键盘的最终高度(包括安全区域插图),请使用UIKeyboardFrame 结束 UserInfoKey。

在iOS 11(特别是iPhone X)中,您可以考虑减去安全区域底部插入。

    NSValue *keyboardEndFrameValue = notification.userInfo[UIKeyboardFrameEndUserInfoKey];
    if (keyboardEndFrameValue != nil) {
        CGRect keyboardSize = [keyboardEndFrameValue CGRectValue];
        _keyboardHeight = keyboardSize.size.height;
        if (@available(iOS 11.0, *)) {
            CGFloat bottomSafeAreaInset = self.view.safeAreaInsets.bottom;
            _keyboardHeight -= bottomSafeAreaInset;
        } else {
            // Fallback on earlier versions
        }
    }

答案 3 :(得分:0)

Swift 4 iPhone X 我不得不稍微调整一下Nathan的答案才能使它起作用。这是100%。

注意:确保已将控件从您的Textview / view的底部约束从情节提要拖动到View Controller中安全区域的底部,并且还已在目标View Controller项目中拖动控件并创建了出口航海家。在我的示例中,我将其命名为bottomConstraint。我的文本输入字段包装在一个视图(messageInputContainerView)中,以允许其他发送按钮对齐等。

Constraint Demo

这是代码:

@objc private func keyboardWillChange(_ notification: Notification) {
    guard let userInfo = (notification as Notification).userInfo, let value = userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue else {
        return
    }

    if ((userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue) != nil {
        let isKeyboardShowing = notification.name == NSNotification.Name.UIKeyboardWillShow
        var newHeight: CGFloat
        if isKeyboardShowing {
            if #available(iOS 11.0, *) {
                newHeight = value.cgRectValue.height - view.safeAreaInsets.bottom
                bottomConstraint.constant = newHeight

            }
        }
        else {
            newHeight = value.cgRectValue.height
            bottomConstraint?.constant = view.safeAreaInsets.bottom + messageInputContainerView.bounds.height

        }
    }
}

答案 4 :(得分:0)

我的问题是我的视图在子视图控制器中。转换CGRect可以解决问题。

@objc private func keyboardWillChangeFrame(notification: NSNotification) {
    guard let userInfo = notification.userInfo, let endFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue, let duration: TimeInterval = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue else {
        return
    }

    let convertedFrame = view.convert(endFrame, from: nil)
    let endFrameY = endFrame.origin.y

    let animationCurveRawNSN = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber
    let animationCurveRaw = animationCurveRawNSN?.uintValue ?? UIViewAnimationOptions.curveEaseInOut.rawValue
    let animationCurve: UIViewAnimationOptions = UIViewAnimationOptions(rawValue: animationCurveRaw)

    if endFrameY >= UIScreen.main.bounds.size.height {
        inputContainerViewBottomLayoutConstraint.constant = 0.0
    }  else {
        var newHeight = view.bounds.size.height - convertedFrame.origin.y
        if #available(iOS 11.0, *) {
            newHeight = newHeight - view.safeAreaInsets.bottom
        }
        inputContainerViewBottomLayoutConstraint.constant = newHeight
    }

    UIView.animate(withDuration: duration, delay: TimeInterval(0), options: animationCurve, animations: {
        self.view.layoutIfNeeded()
    },completion: { _ in
        self.scrollView.scrollToBottom()
    })
}

答案 5 :(得分:0)

这对我有用

TL; DR:self.view.safeAreaInsets.bottom返回0。关键是使用UIApplication.shared.keyWindow.safeAreaInsets.bottom代替[Source]

让replyField为感兴趣的UITextField。

1)在viewDidLoad()中添加观察者。

NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)

2)将类中的全局变量设置为约束。

var textFieldBottomConstraintKeyboardActive : NSLayoutConstraint?
var textFieldBottomConstraintKeyboardInactive : NSLayoutConstraint?

3)在名为viewDidLoad的函数中设置约束。

let replyFieldKeyboardActiveConstraint = self.replyField.bottomAnchor.constraint(
    equalTo: self.view.safeAreaLayoutGuide.bottomAnchor,
    constant: -1 * Constants.DEFAULT_KEYBOARD_HEIGHT /* whatever you want, we will change with actual height later */ + UITabBar.appearance().frame.size.height
)

let replyFieldKeyboardInactiveConstraint = self.replyField.bottomAnchor.constraint(
    equalTo: self.view.safeAreaLayoutGuide.bottomAnchor
)

self.textFieldBottomConstraintKeyboardActive = replyFieldKeyboardActiveConstraint
self.textFieldBottomConstraintKeyboardInactive = replyFieldKeyboardInactiveConstraint

self.textFieldBottomConstraintKeyboardActive?.isActive = false
self.textFieldBottomConstraintKeyboardInactive?.isActive = true

4)定义keyboardWillShow和keyboardWillHide方法。它们的关键是我们如何在keyboardWillShow方法中定义heightOffset。

@objc func keyboardWillShow(notification: NSNotification) {
    guard let userinfo = notification.userInfo else {
        return
    }
    guard let keyboardSize = userinfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else {
        return
    }
    let keyboardFrame = keyboardSize.cgRectValue

    self.view.layoutIfNeeded()

    let heightOffset = keyboardFrame.height - UITabBar.appearance().frame.height - (UIApplication.shared.keyWindow?.safeAreaInsets.bottom ?? 0)

    self.textFieldBottomConstraintKeyboardActive?.constant = -1 * heightOffset

    self.textFieldBottomConstraintKeyboardActive?.isActive = true
    self.textFieldBottomConstraintKeyboardInactive?.isActive = false

    self.view.setNeedsLayout()
}


@objc func keyboardWillHide(notification: NSNotification) {
    self.textFieldBottomConstraintKeyboardActive?.isActive = false
    self.textFieldBottomConstraintKeyboardInactive?.isActive = true
}