简体   繁体   English

iOS-位置视图设置了距屏幕或键盘底部(如果正在显示)的距离,而无需订阅键盘通知

[英]iOS - position view set distance from bottom of screen or keyboard (if it is showing) without subscribing to keyboard notifications

In Android you can use a RelativeLayout and pin a view (such as a button) to the bottom of it using layout_alignParentBottom . 在Android中,您可以使用RelativeLayout并使用layout_alignParentBottom将视图(例如按钮)固定到其底部。 When the keyboard appears, the view will be right above it. 当键盘出现时,视图将在其上方。

Is there a way to do this in iOS without subscribing to keyboard events and adjusting the view's constraint depending on the keyboard height? 有没有一种方法可以在iOS中做到这一点, 而无需订阅键盘事件并根据键盘高度调整视图的约束?

I ask because all the examples I see subscribe to keyboard events. 我问是因为我看到的所有示例都订阅了键盘事件。 But this is rather buggy from my experience. 但这从我的经验来看是相当错误的。 You have to not only subscribe to keyboard events but app wide events too. 您不仅必须订阅键盘事件,还必须订阅应用程序范围的事件。

EDIT: For clarification I am asking about doing exactly what I asked. 编辑:为澄清起见,我正在询问我到底在做什么。 Not adding an inputAccessoryView . 没有添加inputAccessoryView I would like a view (whether it is a button, image, or anything else) to always stay a set distance from the bottom of the screen. 我希望视图(无论是按钮,图像还是其他)始终与屏幕底部保持固定的距离。 When the keyboard appears, I want that view to then be that same set distance from the top of the keyboard. 当键盘出现时,我希望该视图与键盘顶部的距离相同。 If the keyboard disappears, the view will move back down to its original position. 如果键盘消失,则视图将向下移动到其原始位置。

There is currently no other way. 目前没有其他方法。 Showing a keyboard is an app-wide event and is so by design. 显示键盘是应用程序范围内的事件,因此设计使然。 You can get notifications about frame changes and then layout your views accordingly when needed. 您可以获取有关框架更改的通知,然后在需要时相应地布置视图。 There are tools that may help you such as mentioned IQKeyboard but note this tool may and did get buggy on multiple iOS versions and/or devices so you need to keep updating the library. 有一些工具可以为您提供帮助,例如提到的IQKeyboard,但请注意,此工具在多个iOS版本和/或设备上可能确实存在错误,因此您需要不断更新该库。 It is also very hackish but it does work without you needing much work. 它也非常hackhack,但确实不需要很多工作。

From keyboard notifications you can make higher level tools to make things much easier. 通过键盘通知,您可以使用高级工具来简化操作。 What I used some time ago was: 我前一段时间使用的是:

import UIKit

protocol KeyboardManagerDidChangeVisibleDelegate: class {
    func keyboardManagerChangedKeyboardVisible(sender: KeyboardManager, visible: Bool)
}
protocol KeyboardManagerWillChangeFrameDelegate: class {
    func keyboardManagerWillChangeKeyboardFrame(sender: KeyboardManager, from startFrame: CGRect, to endFrame: CGRect)
}
protocol KeyboardManagerDidChangeFrameDelegate: class {
    func keyboardManagerDidChangeKeyboardFrame(sender: KeyboardManager, from startFrame: CGRect, to endFrame: CGRect)
}

class KeyboardManager {

    var keyboardVisible: Bool = false
    var keyboardFrame: CGRect = CGRect.zero

    var visibilityDelegate: KeyboardManagerDidChangeVisibleDelegate?
    var willChangeFrameDelegate: KeyboardManagerWillChangeFrameDelegate?
    var didChangeFrameDelegate: KeyboardManagerDidChangeFrameDelegate?

    static var sharedInstance: KeyboardManager = {
        let manager = KeyboardManager(isShared: true)
        return manager
    }()

    deinit {
        NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillChangeFrame, object: nil)
        NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillShow, object: nil)
        NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillHide, object: nil)
    }

    convenience init() {
        self.init(isShared: false)

    }

    private init(isShared: Bool) {
        attachNotifications()

        if isShared == false {
            keyboardVisible = KeyboardManager.sharedInstance.keyboardVisible
            keyboardFrame = KeyboardManager.sharedInstance.keyboardFrame
        }
    }

    private func attachNotifications() {

        NotificationCenter.default.addObserver(self, selector: #selector(onKeyboardChange), name: .UIKeyboardWillChangeFrame, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(onKeyboardWillShow), name: .UIKeyboardWillShow, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(onKeyboardWillHide), name: .UIKeyboardWillHide, object: nil)

    }

    @objc private func onKeyboardChange(notification: NSNotification) {
        guard let info = notification.userInfo else {
            return
        }
        guard let value: NSValue = info[UIKeyboardFrameEndUserInfoKey] as? NSValue else {
            return
        }
        guard let oldValue: NSValue = info[UIKeyboardFrameBeginUserInfoKey] as? NSValue else {
            return
        }

        let newFrame = value.cgRectValue
        self.keyboardFrame = newFrame

        let oldFrame = oldValue.cgRectValue

        if let durationNumber = info[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber, let keyboardCurveNumber = info[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber {
            let duration = durationNumber.doubleValue
            let keyboardCurve = keyboardCurveNumber.intValue
            let curve: UIViewAnimationCurve = UIViewAnimationCurve(rawValue: keyboardCurve) ?? .linear
            let options = UIViewAnimationOptions(rawValue: UInt(curve.rawValue << 16))

            UIView.animate(withDuration: duration, delay: 0, options: options, animations: {
                self.willChangeFrameDelegate?.keyboardManagerWillChangeKeyboardFrame(sender: self, from: oldFrame, to: newFrame)
            }, completion: { _ in
                self.didChangeFrameDelegate?.keyboardManagerDidChangeKeyboardFrame(sender: self, from: oldFrame, to: newFrame)
            })
        } else {
            self.willChangeFrameDelegate?.keyboardManagerWillChangeKeyboardFrame(sender: self, from: oldFrame, to: newFrame)
            self.didChangeFrameDelegate?.keyboardManagerDidChangeKeyboardFrame(sender: self, from: oldFrame, to: newFrame)
        }
    }
    @objc private func onKeyboardWillShow(notification: NSNotification) {
        self.keyboardVisible = true
        self.visibilityDelegate?.keyboardManagerChangedKeyboardVisible(sender: self, visible: self.keyboardVisible)
    }
    @objc private func onKeyboardWillHide(notification: NSNotification) {
        self.keyboardVisible = false
        self.visibilityDelegate?.keyboardManagerChangedKeyboardVisible(sender: self, visible: self.keyboardVisible)
    }

}

What I do here is subscribe to will appear delegate KeyboardManager.sharedInstance.willChangeFrameDelegate = self in view will appear method of view controller. 我在这里所做的订阅将出现委托KeyboardManager.sharedInstance.willChangeFrameDelegate = self in view将出现在视图控制器的方法中。 Then the implementation is simply: 那么实现就是:

extension MyViewController: KeyboardManagerWillChangeFrameDelegate {

    func keyboardManagerWillChangeKeyboardFrame(sender: KeyboardManager, from startFrame: CGRect, to endFrame: CGRect) {
        panelBottomConstraint?.constant = view.bounds.height-max(0, view.convert(endFrame, from: nil).origin.y)
    }

}

So still using constraints but since manager is imported only once the amount of code when using it is relatively small. 因此仍然使用约束,但是由于管理器仅导入一次,因此使用时的代码量相对较小。 This should already animate your view as well alongside keyboard which is nice. 这应该已经为您的视图以及键盘动画了,这很好。

As for being buggy this procedure has never failed me but in general working with keyboard in iOS is always a pain and is very easy to produce bugs. 至于越野车,这个过程从来没有让我失败,但是总的来说,在iOS中使用键盘总是很痛苦,而且很容易产生错误。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM