【问题标题】:How can I attach view to keyboard top edge using constraints on iOS如何使用 iOS 上的约束将视图附加到键盘顶部边缘
【发布时间】:2021-10-17 04:12:59
【问题描述】:

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

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
])

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

【问题讨论】:

    标签: ios swift autolayout uikit nslayoutconstraint


    【解决方案1】:

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

    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 中启用/禁用它以防止在其他屏幕上进行交互。

    通过收听keyboardWillChangeFrameNotification来更新约束值

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

    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
            )
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-05-22
      • 1970-01-01
      • 1970-01-01
      • 2018-08-01
      • 1970-01-01
      • 2015-09-06
      • 1970-01-01
      • 2016-06-11
      相关资源
      最近更新 更多