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