【问题标题】:Safe Area Insets Disappear when Moving View移动视图时安全区域插图消失
【发布时间】:2019-10-17 08:41:50
【问题描述】:

在我的 iOS 项目中,我有一个带有几个文本字段的视图控制器。为了确保在用户输入文本时控件保持可见,我使用以下代码移动内容并为键盘腾出空间:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    NotificationCenter.default.removeObserver(self)
}

@objc func keyboardWillShow(notification: NSNotification) {
    dPrint(tag: "Safe Area", message: "keyboardWillShow()")
    if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
        if activeField != nil {
            var activeFieldBottomDistance: CGFloat = view.frame.size.height - ... // calculation of position omitted here
            let yOffset: CGFloat = activeFieldBottomDistance
                                    - keyboardSize.height
                                    - view.frame.origin.y
            if yOffset + view.frame.origin.y < 0 {
                moveViewForKeyboard(offset: yOffset)
            }
        }
    }
}

@objc func keyboardWillHide(notification: NSNotification) {
    dPrint(tag: "Safe Area", message: "keyboardWillHide()")
    let yOffset: CGFloat = -view.frame.origin.y
    moveViewForKeyboard(offset: yOffset)
}

func moveViewForKeyboard(offset: CGFloat!) {
    let duration = 0.3
    UIView.beginAnimations(nil, context: nil)
    UIView.setAnimationBeginsFromCurrentState(true)
    UIView.setAnimationDuration(duration)
    view.frame = view.frame.offsetBy(dx: 0, dy: offset)
    UIView.commitAnimations()
    view.setNeedsLayout()
    view.layoutIfNeeded()
}

布局如下(堆栈视图内有文本视图):

我的问题是,当根视图向上移动以为键盘腾出空间时,安全区域的边距会丢失。在横向使用时,键盘出现时,内容会水平扩展,当键盘再次隐藏时,内容会移回安全区域的边界。

下面的代码然后产生下面的输出:

@available(iOS 11.0, *)
override func viewSafeAreaInsetsDidChange() {
    super.viewSafeAreaInsetsDidChange()
    dPrint(tag: "Safe Area", message: "saveAreaInsetsDidChange")
    dPrint(tag: "Safe Area", message: "top:    " + String(describing: view.safeAreaInsets.top))
    dPrint(tag: "Safe Area", message: "right:  " + String(describing: view.safeAreaInsets.right))
    dPrint(tag: "Safe Area", message: "bottom: " + String(describing: view.safeAreaInsets.bottom))
    dPrint(tag: "Safe Area", message: "left:   " + String(describing: view.safeAreaInsets.left))
}

Safe Area saveAreaInsetsDidChange
Safe Area top:    0.0
Safe Area right:  44.0
Safe Area bottom: 21.0
Safe Area left:   44.0
Safe Area keyboardWillShow()
Safe Area saveAreaInsetsDidChange
Safe Area top:    0.0
Safe Area right:  0.0
Safe Area bottom: 0.0
Safe Area left:   0.0
Safe Area keyboardWillHide()
Safe Area saveAreaInsetsDidChange
Safe Area top:    0.0
Safe Area right:  44.0
Safe Area bottom: 21.0
Safe Area left:   44.0

如何移动内容以使其保持在安全区域内,并且它也适用于从 iOS9 开始的旧设备。

(我尝试的选项之一是在根视图下方创建一个额外的视图层并移动它。这不起作用,因为模态显示的内容像对话框一样垂直居中。将中心与除根视图不允许内容移动。)

【问题讨论】:

    标签: ios swift interface-builder ios-autolayout safearealayoutguide


    【解决方案1】:

    您可以使用UnderKeyboard 库来实现。

    因此,我强烈建议您在打开键盘时使用 UIScrollView 来移动您的内容。关注this guide

    或者使用 AutoLayout 底部约束进行底部填充和动画更改其常量。

    例子:

    @IBOutlet var bottomConstraint: NSLayoutConstraint!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    
        NSNotificationCenter.defaultCenter().addObserver(self, selector: "animateWithKeyboard:", name: UIKeyboardWillShowNotification, object: nil)
        NSNotificationCenter.defaultCenter().addObserver(self, selector: "animateWithKeyboard:", name: UIKeyboardWillHideNotification, object: nil)
    }
    
    func animateWithKeyboard(notification: NSNotification) {
    
        let userInfo = notification.userInfo!
        let keyboardHeight = (userInfo[UIKeyboardFrameEndUserInfoKey] as! NSValue).CGRectValue().height
        let duration = userInfo[UIKeyboardAnimationDurationUserInfoKey] as! Double
        let curve = userInfo[UIKeyboardAnimationCurveUserInfoKey] as! UInt
        let moveUp = (notification.name == UIKeyboardWillShowNotification)
    
        bottomConstraint.constant = moveUp ? -keyboardHeight : 0
    
        let options = UIViewAnimationOptions(rawValue: curve << 16)
        UIView.animateWithDuration(duration, delay: 0, options: options, animations: {
            self.view.layoutIfNeeded()
        },
        completion: nil)
    }
    

    【讨论】:

    • 感谢您的详细回答和几个选项。事实上,他们每个人都遵守安全区域的边界。我玩过每个解决方案,并决定使用UIScrollView 和内容插图来解决问题。我很熟悉它,但没有想到使用滚动视图创建我的对话框类型布局,这不是进行这种布局的直观方式。但确实有可能通过一些额外的预防措施,例如以防止在显示键盘时手动滚动。它确实省去了计算精确偏移量的麻烦。
    猜你喜欢
    • 2018-03-12
    • 1970-01-01
    • 2018-04-28
    • 1970-01-01
    • 2020-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多