简体   繁体   中英

iOS How to get keyboard height when the inputview is changed?

I am trying to place a textview just above the keyboard. The keyboard has two views - one default view and one custom view. A button is there to toggle between these two keyboards.

I am trying to use the following code to position the textview just above the keyboard. But it is behaving very weirdly. The keyboardWillShow notification is not getting called all the times.

Note: I wanted it to support for both portrait and landscape.

class ViewController: UIViewController {

    let textView = UITextView()
    let button = UIButton()

    var keyboardHeight: CGFloat = 0
    var keyboardView = UIView()

    override func viewDidLoad() {
        super.viewDidLoad()
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
        button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
        textView.backgroundColor = .lightGray
        button.backgroundColor = .red
        self.view.addSubview(textView)
        self.view.addSubview(button)
    }

    @objc func buttonAction() {
        if self.textView.inputView == nil {
            self.textView.inputView = keyboardView
            self.textView.inputView?.autoresizingMask = .flexibleHeight
            self.textView.reloadInputViews()
            textView.frame = CGRect(x: 0, y: self.view.frame.size.height - self.textView.inputView!.frame.size.height - 50, width: self.view.frame.size.width - 50, height: 50)
            button.frame = CGRect(x: self.view.frame.size.width - 50, y: self.view.frame.size.height - self.textView.inputView!.frame.size.height - 50, width: 50, height: 50)
        } else {
            self.textView.inputView = nil
            self.textView.reloadInputViews()
            textView.frame = CGRect(x: 0, y: self.view.frame.size.height - self.keyboardHeight - 50, width: self.view.frame.size.width - 50, height: 50)
            button.frame = CGRect(x: self.view.frame.size.width - 50, y: self.view.frame.size.height - self.keyboardHeight - 50, width: 50, height: 50)
        }
    }

    override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()
        textView.frame = CGRect(x: 0, y: self.view.frame.size.height - self.keyboardHeight - 50, width: self.view.frame.size.width - 50, height: 50)
        button.frame = CGRect(x: self.view.frame.size.width - 50, y: self.view.frame.size.height - self.keyboardHeight - 50, width: 50, height: 50)
    }

    @objc func keyboardWillShow(notification: Notification) {
        if let userInfo = notification.userInfo {
            if let keyboardFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
                self.keyboardHeight = keyboardFrame.size.height
                textView.frame = CGRect(x: 0, y: self.view.frame.size.height - self.keyboardHeight - 50, width: self.view.frame.size.width - 50, height: 50)
                button.frame = CGRect(x: self.view.frame.size.width - 50, y: self.view.frame.size.height - self.keyboardHeight - 50, width: 50, height: 50)
            }
        }
    }
}

If you wanna get the height of Keyboard when keyboard frame changes because of you toggling between custom keyboard and system keyboard, you can achieve it many ways.

I personally dont prefer notifications they always end up giving me a discrete values, though most of the realtime cases they are good enough when you need to modify the UI as keyboard frame changes in real time (Like try dismissing keyboard on scrolling tableView like whats app where keyboard would go down with your finger as u move and u need to align views in realtime when keyboards scrolls down)

Anyway I believe the approach explained below should also help u to get accurate keyboard height when toggled

Step 1:

Create a subclass of UIView which we will be adding as input accessory view to textView/textField later

protocol KeyBoardObserverProtocol : NSObjectProtocol {
    func keyboardFrameChanged(frame : CGRect)
}

class KeyboardObserverAccessoryView: UIView {
    weak var keyboardDelegate:KeyBoardObserverProtocol? = nil
    private var kvoContext: UInt8 = 1

    override func willMove(toSuperview newSuperview: UIView?) {
        if newSuperview == nil {
            self.superview?.removeObserver(self, forKeyPath: "center")
        }
        else{
            newSuperview?.addObserver(self, forKeyPath: "center", options: [NSKeyValueObservingOptions.new, NSKeyValueObservingOptions.initial], context: &kvoContext)
        }
    }

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if let theChange = change as [NSKeyValueChangeKey : AnyObject]?{
            if theChange[NSKeyValueChangeKey.newKey] != nil{
                if self.keyboardDelegate != nil && self.superview?.frame != nil {
                    self.keyboardDelegate?.keyboardFrameChanged(frame: (self.superview?.frame)!)
                }
            }
        }
    }
}

Though code looks big and messy whats inside is pretty easy to understand. All it does is it start obseving the parent view frame using KVO once it receives the call willMove(toSuperview newSuperview: UIView?) because thats when you know ur view is moved to parent and u know that now u need to monitor the parent's frame.

And every time your KVO triggers the new value u inform it to the interred party using the delegate

How to use it?

    keyBoardObserverAccessoryView = KeyboardObserverAccessoryView(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
    keyBoardObserverAccessoryView.keyboardDelegate = self
    self.textView.inputAccessoryView = keyBoardObserverAccessoryView

Thats it :) Now if you wanna provide your custom input accessory view in future just make sure u extend from KeyboardObserverAccessoryView n enjoy the benefits

Hope it helps :)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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