Home > OS >  Moving UIView up only when the keyboard blocks the UITextField / UITextView
Moving UIView up only when the keyboard blocks the UITextField / UITextView

Time:10-12

This has been asked a few times before: 1 2 3 4

These threads did not provide the solution I was looking for, and are getting a bit outdated with the release of Swift 5, so I've posted my solution below.

CodePudding user response:

This is my current solution:

import UIKit

// For UITextViews, exact same code, but using UITextViewDelegate
class viewController: UIViewController, UITextFieldDelegate {
    @IBOutlet weak var textField: UITextField!
    
    // Keeps track of our editing frame
    // Kept it as a CGRect so that it can work with both UITextFields and UITextViews
    var editingFrame: CGRect?

    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Set the delegate for the textField(s)
        textField.delegate = self
        
        // Add notification observers for keyboard
        NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)
    }
    
    @objc func keyboardWillShow(notification: Notification) {
        if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect) {
            if let editingFrame = editingFrame {
                let offset = editingFrame.maxY - keyboardSize.minY

                if offset > self.view.frame.origin.y {
                    UIView.animate(withDuration: 1, delay: 0, options: .curveEaseInOut) {
                        self.view.frame.origin.y -= offset
                    }
                } else {
                    if self.view.frame.origin.y != 0 {
                        UIView.animate(withDuration: 1, delay: 0, options: .curveEaseInOut) {
                            self.view.frame.origin.y = 0
                        }
                    }
                }
            }
        }
    }
    
    @objc func keyboardWillHide(notification: Notification) {
        if self.view.frame.origin.y != 0 {
            UIView.animate(withDuration: 1, delay: 0, options: .curveEaseInOut) {
                self.view.frame.origin.y = 0
            }
        }
    }
    
    func textFieldDidBeginEditing(_ textField: UITextField) {
        editingFrame = CGRect(x: 0, y: 0, width: textField.frame.width, height: textField.frame.height)
        
        // This is the bit that is important, especially if you have
        // your textfield inside another view (UIStackViews for example)
        // The loop essentially goes through each successive superview the textfield has
        // and redefines the centre in that superview's frame of reference
        var currentView: UIView? = textField.superview
        while currentView != nil {
            editingFrame?.origin.x  = currentView?.frame.origin.x ?? CGFloat(0.0)
            editingFrame?.origin.y  = currentView?.frame.origin.y ?? CGFloat(0.0)
            currentView = currentView?.superview
        }
    }
    
    func textFieldDidEndEditing(_ textField: UITextField) {
        editingFrame = nil
    }
}

CodePudding user response:

I recommend using IQKeyboardManagerSwift (or IQKeyboardManager written in ObjC) library that you can install with cocoapods, all tutorials included in link. For me it works better that custom solutions. Here are 2 versions of the library, ObjC and Swift :

Swift verion: https://cocoapods.org/pods/IQKeyboardManagerSwift

ObjC version: https://cocoapods.org/pods/IQKeyboardManager

  • Related