如何使用 iOS 上的约束将视图附加到键盘顶部边缘 [英] How can I attach view to keyboard top edge using constraints on iOS

查看:30
本文介绍了如何使用 iOS 上的约束将视图附加到键盘顶部边缘的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个简单的视图附加到视图控制器的底部:

I have a simple view attached to the bottom of view controller:

let view = UIView()
view.backgroundColor = .gray
view.translatesAutoresizingMaskIntoConstraints = false
let viewBottomPositionConstraint = view.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor)
NSLayoutConstraint.activate([
    view.heightAnchor.constraint(equalToConstant: 100),
    view.leftAnchor.constraint(equalTo: self.view.leftAnchor),
    view.rightAnchor.constraint(equalTo: self.view.rightAnchor),
    viewBottomPositionConstraint
])

当键盘在键盘动画后出现/消失时,我需要该视图上下移动

I need that view to go up and down when keyboard appears/disappears following keyboard animation

推荐答案

我已经创建了KeyboardNotifier,用法很简单:

I've created KeyboardNotifier, usage is pretty simple:

var keyboardNotifier: KeyboardNotifier!

override func viewDidLoad() {
    super.viewDidLoad()
    
    keyboardNotifier = KeyboardNotifier(parentView: view, constraint: viewBottomPositionConstraint)
}

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    keyboardNotifier.enabled = true

override func viewDidDisappear(_ animated: Bool) {
    keyboardNotifier.enabled = false
    super.viewDidDisappear(animated)
}

它应该在 viewWillAppear/viewDidDisappear 中启用/禁用以防止在其他屏幕上交互.

It should be enabled/disabled in viewWillAppear/viewDidDisappear to prevent interactions on other screens.

通过监听 keyboardWillChangeFrameNotification

我不得不将它从键盘创建的动画中移出以防止出现故障,并同时更新我自己动画中的约束.

I had to move it out of the animation created by keyboard to prevent glitches, and updating constraint in my own animation in parallel.

final class KeyboardNotifier {
    var enabled: Bool = true {
        didSet {
            setNeedsUpdateConstraint()
        }
    }
    
    init(
        parentView: UIView,
        constraint: NSLayoutConstraint
    ) {
        self.parentView = parentView
        self.constraint = constraint
        
        baseConstant = constraint.constant
        notificationObserver = NotificationCenter.default
            .addObserver(
                forName: UIResponder.keyboardWillChangeFrameNotification,
                object: nil,
                queue: .main
            ) { [weak self] in
                self?.keyboardEndFrame = ($0.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
                self?.setNeedsUpdateConstraint(animationDuration: UIView.inheritedAnimationDuration)
            }
    }
    
    private weak var parentView: UIView?
    private weak var constraint: NSLayoutConstraint?
    private let baseConstant: CGFloat
    private var notificationObserver: NSObjectProtocol!
    private var keyboardEndFrame: CGRect?
    private var latestAnimationDuration: TimeInterval?
    
    private func setNeedsUpdateConstraint(animationDuration: TimeInterval = 0) {
        guard
            latestAnimationDuration == nil
            || animationDuration > latestAnimationDuration!
            else { return }
        let shouldUpdate = latestAnimationDuration == nil
        latestAnimationDuration = animationDuration
        if shouldUpdate {
            DispatchQueue.main.async {
                self.updateConstraint()
            }
        }
    }
    
    private func updateConstraint() {
        defer {
            latestAnimationDuration = nil
        }
        guard
            let latestAnimationDuration = latestAnimationDuration,
            enabled,
            let keyboardEndFrame = keyboardEndFrame,
            let parentView = parentView,
            let constraint = constraint
            else { return }
        
        UIView.performWithoutAnimation {
            parentView.layoutIfNeeded()
        }
        let isParentFirstItem = constraint.firstItem is UILayoutGuide || constraint.firstItem === parentView
        let followsLayoutGuide = constraint.firstItem is UILayoutGuide || constraint.secondItem is UILayoutGuide
        let multiplierSign: CGFloat = isParentFirstItem ? 1 : -1
        let screenHeight = UIScreen.main.bounds.height
        if keyboardEndFrame.minY >= screenHeight {
            constraint.constant = baseConstant
        } else {
            let safeAreaInsets = (followsLayoutGuide ? parentView.safeAreaInsets.bottom : 0)
            // if our constraint makes view invisible when keyboard is hidden, we need to ignore it
            let fixedBaseConstant = max(multiplierSign * baseConstant, 0)
            constraint.constant = multiplierSign * (screenHeight - keyboardEndFrame.minY - safeAreaInsets + fixedBaseConstant)
        }
        
        UIView.animate(
            withDuration: latestAnimationDuration,
            delay: 0,
            options: .beginFromCurrentState,
            animations: { parentView.layoutIfNeeded() },
            completion: nil
        )
    }
}

这篇关于如何使用 iOS 上的约束将视图附加到键盘顶部边缘的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆