I'm using Swift for programing with iOS and I'm using this code to move the UITextField
, but it does not work. I call the function keyboardWillShow
correctly, but the textfield doesn't move. I'm using autolayout.
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name:UIKeyboardWillShowNotification, object: nil);
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name:UIKeyboardWillHideNotification, object: nil);
}
deinit {
NSNotificationCenter.defaultCenter().removeObserver(self);
}
func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
//let contentInsets = UIEdgeInsets(top: 0, left: 0, bottom: keyboardSize.height, right: 0)
var frame = self.ChatField.frame
frame.origin.y = frame.origin.y - keyboardSize.height + 167
self.chatField.frame = frame
println("asdasd")
}
}
This question is related to
ios
swift
cocoa-touch
keyboard
uitextfield
Just enclose your textbox inside a view and then override inputAccessoryView returning the view. Important: Your view should be created programmatically. Do not use @IBOutlets.
override var inputAccessoryView: UIView? {
get {
return newlyProgramaticallyCreatedView
}}
This is useful when textfield superview is view
class AdminLoginViewController: UIViewController,
UITextFieldDelegate{
@IBOutlet weak var txtUserName: UITextField!
@IBOutlet weak var txtUserPassword: UITextField!
@IBOutlet weak var btnAdminLogin: UIButton!
private var activeField : UIView?
var param:String!
var adminUser : Admin? = nil
var kbHeight: CGFloat!
override func viewDidLoad()
{
self.addKeyBoardObserver()
self.addGestureForHideKeyBoard()
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func addGestureForHideKeyBoard()
{
let tapGesture = UITapGestureRecognizer(target: self, action: Selector("hideKeyboard"))
tapGesture.cancelsTouchesInView = false
view.addGestureRecognizer(tapGesture)
}
func hideKeyboard() {
self.view.endEditing(true)
}
func addKeyBoardObserver(){
NSNotificationCenter.defaultCenter().addObserver(self, selector: "willChangeKeyboardFrame:",
name:UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "willChangeKeyboardFrame:",
name:UIKeyboardWillHideNotification, object: nil)
}
func removeObserver(){
NSNotificationCenter.defaultCenter().removeObserver(self)
}
//MARK:- textfiled Delegate
func textFieldShouldBeginEditing(textField: UITextField) -> Bool
{
activeField = textField
return true
}
func textFieldShouldEndEditing(textField: UITextField) -> Bool
{
if activeField == textField
{
activeField = nil
}
return true
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
if txtUserName == textField
{
txtUserPassword.becomeFirstResponder()
}
else if (textField == txtUserPassword)
{
self.btnAdminLoginAction(nil)
}
return true;
}
func willChangeKeyboardFrame(aNotification : NSNotification)
{
if self.activeField != nil && self.activeField!.isFirstResponder()
{
if let keyboardSize = (aNotification.userInfo![UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue()
{
let dy = (self.activeField?.superview?.convertRect((self.activeField?.frame)!, toView: view).origin.y)!
let height = (self.view.frame.size.height - keyboardSize.size.height)
if dy > height
{
var frame = self.view.frame
frame.origin.y = -((dy - height) + (self.activeField?.frame.size.height)! + 20)
self.view.frame = frame
}
}
}
else
{
var frame = self.view.frame
frame.origin.y = 0
self.view.frame = frame
}
} }
Such simple UIViewController
extension can be used
//MARK: - Observers
extension UIViewController {
func addObserverForNotification(notificationName: String, actionBlock: (NSNotification) -> Void) {
NSNotificationCenter.defaultCenter().addObserverForName(notificationName, object: nil, queue: NSOperationQueue.mainQueue(), usingBlock: actionBlock)
}
func removeObserver(observer: AnyObject, notificationName: String) {
NSNotificationCenter.defaultCenter().removeObserver(observer, name: notificationName, object: nil)
}
}
//MARK: - Keyboard observers
extension UIViewController {
typealias KeyboardHeightClosure = (CGFloat) -> ()
func addKeyboardChangeFrameObserver(willShow willShowClosure: KeyboardHeightClosure?,
willHide willHideClosure: KeyboardHeightClosure?) {
NSNotificationCenter.defaultCenter().addObserverForName(UIKeyboardWillChangeFrameNotification,
object: nil, queue: NSOperationQueue.mainQueue(), usingBlock: { [weak self](notification) in
if let userInfo = notification.userInfo,
let frame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.CGRectValue(),
let duration = userInfo[UIKeyboardAnimationDurationUserInfoKey] as? Double,
let c = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? UInt,
let kFrame = self?.view.convertRect(frame, fromView: nil),
let kBounds = self?.view.bounds {
let animationType = UIViewAnimationOptions(rawValue: c)
let kHeight = kFrame.size.height
UIView.animateWithDuration(duration, delay: 0, options: animationType, animations: {
if CGRectIntersectsRect(kBounds, kFrame) { // keyboard will be shown
willShowClosure?(kHeight)
} else { // keyboard will be hidden
willHideClosure?(kHeight)
}
}, completion: nil)
} else {
print("Invalid conditions for UIKeyboardWillChangeFrameNotification")
}
})
}
func removeKeyboardObserver() {
removeObserver(self, notificationName: UIKeyboardWillChangeFrameNotification)
}
}
Example of usage
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
removeKeyboardObserver()
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
addKeyboardChangeFrameObserver(willShow: { [weak self](height) in
//Update constraints here
self?.view.setNeedsUpdateConstraints()
}, willHide: { [weak self](height) in
//Reset constraints here
self?.view.setNeedsUpdateConstraints()
})
}
Swift 4 solution
//MARK: - Observers
extension UIViewController {
func addObserverForNotification(_ notificationName: Notification.Name, actionBlock: @escaping (Notification) -> Void) {
NotificationCenter.default.addObserver(forName: notificationName, object: nil, queue: OperationQueue.main, using: actionBlock)
}
func removeObserver(_ observer: AnyObject, notificationName: Notification.Name) {
NotificationCenter.default.removeObserver(observer, name: notificationName, object: nil)
}
}
//MARK: - Keyboard handling
extension UIViewController {
typealias KeyboardHeightClosure = (CGFloat) -> ()
func addKeyboardChangeFrameObserver(willShow willShowClosure: KeyboardHeightClosure?,
willHide willHideClosure: KeyboardHeightClosure?) {
NotificationCenter.default.addObserver(forName: NSNotification.Name.UIKeyboardWillChangeFrame,
object: nil, queue: OperationQueue.main, using: { [weak self](notification) in
if let userInfo = notification.userInfo,
let frame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue,
let duration = userInfo[UIKeyboardAnimationDurationUserInfoKey] as? Double,
let c = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? UInt,
let kFrame = self?.view.convert(frame, from: nil),
let kBounds = self?.view.bounds {
let animationType = UIViewAnimationOptions(rawValue: c)
let kHeight = kFrame.size.height
UIView.animate(withDuration: duration, delay: 0, options: animationType, animations: {
if kBounds.intersects(kFrame) { // keyboard will be shown
willShowClosure?(kHeight)
} else { // keyboard will be hidden
willHideClosure?(kHeight)
}
}, completion: nil)
} else {
print("Invalid conditions for UIKeyboardWillChangeFrameNotification")
}
})
}
func removeKeyboardObserver() {
removeObserver(self, notificationName: NSNotification.Name.UIKeyboardWillChangeFrame)
}
}
Swift 4.2
//MARK: - Keyboard handling
extension UIViewController {
func addObserverForNotification(_ notificationName: Notification.Name, actionBlock: @escaping (Notification) -> Void) {
NotificationCenter.default.addObserver(forName: notificationName, object: nil, queue: OperationQueue.main, using: actionBlock)
}
func removeObserver(_ observer: AnyObject, notificationName: Notification.Name) {
NotificationCenter.default.removeObserver(observer, name: notificationName, object: nil)
}
typealias KeyboardHeightClosure = (CGFloat) -> ()
func removeKeyboardObserver() {
removeObserver(self, notificationName: UIResponder.keyboardWillChangeFrameNotification)
}
func addKeyboardChangeFrameObserver(willShow willShowClosure: KeyboardHeightClosure?,
willHide willHideClosure: KeyboardHeightClosure?) {
NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillChangeFrameNotification,
object: nil, queue: OperationQueue.main, using: { [weak self](notification) in
if let userInfo = notification.userInfo,
let frame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue,
let duration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double,
let c = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? UInt,
let kFrame = self?.view.convert(frame, from: nil),
let kBounds = self?.view.bounds {
let animationType = UIView.AnimationOptions(rawValue: c)
let kHeight = kFrame.size.height
UIView.animate(withDuration: duration, delay: 0, options: animationType, animations: {
if kBounds.intersects(kFrame) { // keyboard will be shown
willShowClosure?(kHeight)
} else { // keyboard will be hidden
willHideClosure?(kHeight)
}
}, completion: nil)
} else {
print("Invalid conditions for UIKeyboardWillChangeFrameNotification")
}
})
}
}
Easiest way that doesn't require any code:
The object will auto-move up with the keyboard, in sync.
The Swift 4 solution I use, takes in keyboard size. Replace serverStatusStackView
with whatever view you care about, e.g.: self.view
:
deinit {
NotificationCenter.default.removeObserver(self)
}
@objc func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
serverStatusStackView.frame.origin.y = keyboardSize.height * 2 - serverStatusStackView.frame.height
}
}
@objc func keyboardWillHide(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
serverStatusStackView.frame.origin.y += keyboardSize.height
}
}
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
If you're using Auto Layout, I assume you've set the Bottom Space to Superview constraint. If that's the case, you simply have to update the constraint's value. Here's how you do it with a little bit of animation.
func keyboardWasShown(notification: NSNotification) {
let info = notification.userInfo!
let keyboardFrame: CGRect = (info[UIKeyboardFrameEndUserInfoKey] as! NSValue).CGRectValue()
UIView.animateWithDuration(0.1, animations: { () -> Void in
self.bottomConstraint.constant = keyboardFrame.size.height + 20
})
}
The hardcoded 20 is added only to pop the textfield above the keyboard just a bit. Otherwise the keyboard's top margin and textfield's bottom margin would be touching.
When the keyboard is dismissed, reset the constraint's value to its original one.
Swift 4.1,
Use TPKeyBoardAvoiding class for achieving this. This works fine with UIScrollView, UICollectionView, UITableView.
Just assign this class to your scrollview, collectionview or tableview in storyboard or create its object programmatically. All textfield or textviews inside TPKeyboardAvoiding scrollview will adjust automatically when the keyboard appears and disappears.
Here is the link for TPKeyboardAvoiding
TPKeyboardAvoiding for Swift 4.1,
import Foundation
import UIKit
// MARK: - TableView
class TPKeyboardAvoidingTableView:UITableView,UITextFieldDelegate, UITextViewDelegate {
override var frame:CGRect{
willSet{
super.frame = frame
}
didSet{
if hasAutomaticKeyboardAvoidingBehaviour() {return}
TPKeyboardAvoiding_updateContentInset()
}
}
override var contentSize:CGSize{
willSet(newValue){
if hasAutomaticKeyboardAvoidingBehaviour() {
super.contentSize = newValue
return
}
if newValue.equalTo(self.contentSize)
{
return
}
super.contentSize = newValue
self.TPKeyboardAvoiding_updateContentInset()
}
// didSet{
// self.TPKeyboardAvoiding_updateContentInset()
// }
}
override init(frame: CGRect, style: UITableViewStyle) {
super.init(frame: frame, style: style)
self.setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.setup()
}
override func awakeFromNib() {
setup()
}
deinit{
NotificationCenter.default.removeObserver(self)
}
func hasAutomaticKeyboardAvoidingBehaviour()->Bool
{
if #available(iOS 8.3, *) {
if self.delegate is UITableViewController
{
return true
}
}
return false
}
func focusNextTextField()->Bool
{
return self.TPKeyboardAvoiding_focusNextTextField()
}
@objc func scrollToActiveTextField()
{
return self.TPKeyboardAvoiding_scrollToActiveTextField()
}
override func willMove(toSuperview newSuperview: UIView?) {
super.willMove(toSuperview: newSuperview)
if newSuperview != nil {
NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView(_:)), object: self)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
self.TPKeyboardAvoiding_findFirstResponderBeneathView(self)?.resignFirstResponder()
super.touchesEnded(touches, with: event)
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if !self.focusNextTextField()
{
textField.resignFirstResponder()
}
return true
}
override func layoutSubviews() {
super.layoutSubviews()
NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView(_:)), object: self)
Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView(_:)), userInfo: nil, repeats: false)
}
}
private extension TPKeyboardAvoidingTableView
{
func setup()
{
if self.hasAutomaticKeyboardAvoidingBehaviour() { return }
NotificationCenter.default.addObserver(self,
selector: #selector(TPKeyboardAvoiding_keyboardWillShow(_:)),
name: NSNotification.Name.UIKeyboardWillChangeFrame,
object: nil)
NotificationCenter.default.addObserver(self,
selector: #selector(TPKeyboardAvoiding_keyboardWillHide(_:)),
name: NSNotification.Name.UIKeyboardWillHide,
object: nil)
NotificationCenter.default.addObserver(self,
selector: #selector(scrollToActiveTextField),
name: NSNotification.Name.UITextViewTextDidBeginEditing,
object: nil)
NotificationCenter.default.addObserver(self,
selector: #selector(scrollToActiveTextField),
name: NSNotification.Name.UITextFieldTextDidBeginEditing,
object: nil)
}
}
// MARK: - CollectionView
class TPKeyboardAvoidingCollectionView:UICollectionView,UITextViewDelegate {
override var contentSize:CGSize{
willSet(newValue){
if newValue.equalTo(self.contentSize)
{
return
}
super.contentSize = newValue
self.TPKeyboardAvoiding_updateContentInset()
}
// didSet{
// self.TPKeyboardAvoiding_updateContentInset()
// }
}
override var frame:CGRect{
willSet{
super.frame = frame
}
didSet{
self.TPKeyboardAvoiding_updateContentInset()
}
}
// override init(frame: CGRect) {
// super.init(frame: frame)
// }
override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
super.init(frame: frame, collectionViewLayout: layout)
setup()
}
required init?(coder aDecoder: NSCoder) {
// fatalError("init(coder:) has not been implemented")
super.init(coder: aDecoder)
self.setup()
}
override func awakeFromNib() {
setup()
}
deinit{
NotificationCenter.default.removeObserver(self)
}
func focusNextTextField()->Bool
{
return self.TPKeyboardAvoiding_focusNextTextField()
}
@objc func scrollToActiveTextField()
{
return self.TPKeyboardAvoiding_scrollToActiveTextField()
}
override func willMove(toSuperview newSuperview: UIView?) {
super.willMove(toSuperview: newSuperview)
if newSuperview != nil {
NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView(_:)), object: self)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
self.TPKeyboardAvoiding_findFirstResponderBeneathView(self)?.resignFirstResponder()
super.touchesEnded(touches, with: event)
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if !self.focusNextTextField()
{
textField.resignFirstResponder()
}
return true
}
override func layoutSubviews() {
super.layoutSubviews()
NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView(_:)), object: self)
Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView(_:)), userInfo: nil, repeats: false)
}
}
private extension TPKeyboardAvoidingCollectionView
{
func setup()
{
NotificationCenter.default.addObserver(self,
selector: #selector(TPKeyboardAvoiding_keyboardWillShow(_:)),
name: NSNotification.Name.UIKeyboardWillChangeFrame,
object: nil)
NotificationCenter.default.addObserver(self,
selector: #selector(TPKeyboardAvoiding_keyboardWillHide(_:)),
name: NSNotification.Name.UIKeyboardWillHide,
object: nil)
NotificationCenter.default.addObserver(self,
selector: #selector(scrollToActiveTextField),
name: NSNotification.Name.UITextViewTextDidBeginEditing,
object: nil)
NotificationCenter.default.addObserver(self,
selector: #selector(scrollToActiveTextField),
name: NSNotification.Name.UITextFieldTextDidBeginEditing,
object: nil)
}
}
// MARK: - ScrollView
class TPKeyboardAvoidingScrollView:UIScrollView,UITextFieldDelegate,UITextViewDelegate
{
override var contentSize:CGSize{
didSet{
self.TPKeyboardAvoiding_updateFromContentSizeChange()
}
}
override var frame:CGRect{
didSet{
self.TPKeyboardAvoiding_updateContentInset()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
self.setup()
}
override func awakeFromNib() {
setup()
}
func contentSizeToFit()
{
self.contentSize = self.TPKeyboardAvoiding_calculatedContentSizeFromSubviewFrames()
}
func focusNextTextField() ->Bool
{
return self.TPKeyboardAvoiding_focusNextTextField()
}
@objc func scrollToActiveTextField()
{
return self.TPKeyboardAvoiding_scrollToActiveTextField()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.setup()
}
deinit{
NotificationCenter.default.removeObserver(self)
}
override func willMove(toSuperview newSuperview: UIView?) {
super.willMove(toSuperview: newSuperview)
if newSuperview != nil {
NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView(_:)), object: self)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
self.TPKeyboardAvoiding_findFirstResponderBeneathView(self)?.resignFirstResponder()
super.touchesEnded(touches, with: event)
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if !self.focusNextTextField()
{
textField.resignFirstResponder()
}
return true
}
override func layoutSubviews() {
super.layoutSubviews()
NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView(_:)), object: self)
Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView(_:)), userInfo: nil, repeats: false)
}
}
private extension TPKeyboardAvoidingScrollView
{
func setup()
{
NotificationCenter.default.addObserver(self,
selector: #selector(TPKeyboardAvoiding_keyboardWillShow(_:)),
name: NSNotification.Name.UIKeyboardWillChangeFrame,
object: nil)
NotificationCenter.default.addObserver(self,
selector: #selector(TPKeyboardAvoiding_keyboardWillHide(_:)),
name: NSNotification.Name.UIKeyboardWillHide,
object: nil)
NotificationCenter.default.addObserver(self,
selector: #selector(scrollToActiveTextField),
name: NSNotification.Name.UITextViewTextDidBeginEditing,
object: nil)
NotificationCenter.default.addObserver(self,
selector: #selector(scrollToActiveTextField),
name: NSNotification.Name.UITextFieldTextDidBeginEditing,
object: nil)
}
}
// MARK: - Process Event
let kCalculatedContentPadding:CGFloat = 10;
let kMinimumScrollOffsetPadding:CGFloat = 20;
extension UIScrollView
{
@objc func TPKeyboardAvoiding_keyboardWillShow(_ notification:Notification)
{
guard let userInfo = notification.userInfo else { return }
guard let rectNotification = notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue else
{
return
}
let keyboardRect = self.convert(rectNotification.cgRectValue , from: nil)
if keyboardRect.isEmpty
{
return
}
let state = self.keyboardAvoidingState()
guard let firstResponder = self.TPKeyboardAvoiding_findFirstResponderBeneathView(self) else { return}
state.keyboardRect = keyboardRect
if !state.keyboardVisible
{
state.priorInset = self.contentInset
state.priorScrollIndicatorInsets = self.scrollIndicatorInsets
state.priorPagingEnabled = self.isPagingEnabled
}
state.keyboardVisible = true
self.isPagingEnabled = false
if self is TPKeyboardAvoidingScrollView
{
state.priorContentSize = self.contentSize
if self.contentSize.equalTo(CGSize.zero)
{
self.contentSize = self.TPKeyboardAvoiding_calculatedContentSizeFromSubviewFrames()
}
}
let duration = userInfo[UIKeyboardAnimationDurationUserInfoKey] as? Float ?? 0.0
let curve = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? Int ?? 0
let options = UIViewAnimationOptions(rawValue: UInt(curve))
UIView.animate(withDuration: TimeInterval(duration),
delay: 0,
options: options,
animations: { [weak self]() -> Void in
if let actualSelf = self
{
actualSelf.contentInset = actualSelf.TPKeyboardAvoiding_contentInsetForKeyboard()
let viewableHeight = actualSelf.bounds.size.height - actualSelf.contentInset.top - actualSelf.contentInset.bottom
let point = CGPoint(x: actualSelf.contentOffset.x, y: actualSelf.TPKeyboardAvoiding_idealOffsetForView(firstResponder, viewAreaHeight: viewableHeight))
actualSelf.setContentOffset(point, animated: false)
actualSelf.scrollIndicatorInsets = actualSelf.contentInset
actualSelf.layoutIfNeeded()
}
}) { (finished) -> Void in
}
}
@objc func TPKeyboardAvoiding_keyboardWillHide(_ notification:Notification)
{
guard let userInfo = notification.userInfo else { return }
guard let rectNotification = userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue else
{
return
}
let keyboardRect = self.convert(rectNotification.cgRectValue , from: nil)
if keyboardRect.isEmpty
{
return
}
let state = self.keyboardAvoidingState()
if !state.keyboardVisible
{
return
}
state.keyboardRect = CGRect.zero
state.keyboardVisible = false
let duration = userInfo[UIKeyboardAnimationDurationUserInfoKey] as? Float ?? 0.0
let curve = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? Int ?? 0
let options = UIViewAnimationOptions(rawValue: UInt(curve))
UIView.animate(withDuration: TimeInterval(duration),
delay: 0,
options: options,
animations: { [weak self]() -> Void in
if let actualSelf = self
{
if actualSelf is TPKeyboardAvoidingScrollView {
actualSelf.contentSize = state.priorContentSize
actualSelf.contentInset = state.priorInset
actualSelf.scrollIndicatorInsets = state.priorScrollIndicatorInsets
actualSelf.isPagingEnabled = state.priorPagingEnabled
actualSelf.layoutIfNeeded()
}
}
}) { (finished) -> Void in
}
}
func TPKeyboardAvoiding_updateFromContentSizeChange()
{
let state = self.keyboardAvoidingState()
if state.keyboardVisible
{
state.priorContentSize = self.contentSize
}
}
func TPKeyboardAvoiding_focusNextTextField() ->Bool
{
guard let firstResponder = self.TPKeyboardAvoiding_findFirstResponderBeneathView(self) else { return false}
guard let view = self.TPKeyboardAvoiding_findNextInputViewAfterView(firstResponder, beneathView: self) else { return false}
Timer.scheduledTimer(timeInterval: 0.1, target: view, selector: #selector(becomeFirstResponder), userInfo: nil, repeats: false)
return true
}
func TPKeyboardAvoiding_scrollToActiveTextField()
{
let state = self.keyboardAvoidingState()
if !state.keyboardVisible { return }
let visibleSpace = self.bounds.size.height - self.contentInset.top - self.contentInset.bottom
let idealOffset = CGPoint(x: 0,
y: self.TPKeyboardAvoiding_idealOffsetForView(self.TPKeyboardAvoiding_findFirstResponderBeneathView(self),
viewAreaHeight: visibleSpace))
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Double((Int64)(0 * NSEC_PER_SEC)) / Double(NSEC_PER_SEC)) {[weak self] () -> Void in
self?.setContentOffset(idealOffset, animated: true)
}
}
//Helper
func TPKeyboardAvoiding_findFirstResponderBeneathView(_ view:UIView) -> UIView?
{
for childView in view.subviews
{
if childView.responds(to: #selector(getter: isFirstResponder)) && childView.isFirstResponder
{
return childView
}
let result = TPKeyboardAvoiding_findFirstResponderBeneathView(childView)
if result != nil
{
return result
}
}
return nil
}
func TPKeyboardAvoiding_updateContentInset()
{
let state = self.keyboardAvoidingState()
if state.keyboardVisible
{
self.contentInset = self.TPKeyboardAvoiding_contentInsetForKeyboard()
}
}
func TPKeyboardAvoiding_calculatedContentSizeFromSubviewFrames() ->CGSize
{
let wasShowingVerticalScrollIndicator = self.showsVerticalScrollIndicator
let wasShowingHorizontalScrollIndicator = self.showsHorizontalScrollIndicator
self.showsVerticalScrollIndicator = false
self.showsHorizontalScrollIndicator = false
var rect = CGRect.zero
for view in self.subviews
{
rect = rect.union(view.frame)
}
rect.size.height += kCalculatedContentPadding
self.showsVerticalScrollIndicator = wasShowingVerticalScrollIndicator
self.showsHorizontalScrollIndicator = wasShowingHorizontalScrollIndicator
return rect.size
}
func TPKeyboardAvoiding_idealOffsetForView(_ view:UIView?,viewAreaHeight:CGFloat) -> CGFloat
{
let contentSize = self.contentSize
var offset:CGFloat = 0.0
let subviewRect = view != nil ? view!.convert(view!.bounds, to: self) : CGRect.zero
var padding = (viewAreaHeight - subviewRect.height)/2
if padding < kMinimumScrollOffsetPadding
{
padding = kMinimumScrollOffsetPadding
}
offset = subviewRect.origin.y - padding - self.contentInset.top
if offset > (contentSize.height - viewAreaHeight)
{
offset = contentSize.height - viewAreaHeight
}
if offset < -self.contentInset.top
{
offset = -self.contentInset.top
}
return offset
}
func TPKeyboardAvoiding_contentInsetForKeyboard() -> UIEdgeInsets
{
let state = self.keyboardAvoidingState()
var newInset = self.contentInset;
let keyboardRect = state.keyboardRect
newInset.bottom = keyboardRect.size.height - max(keyboardRect.maxY - self.bounds.maxY, 0)
return newInset
}
func TPKeyboardAvoiding_viewIsValidKeyViewCandidate(_ view:UIView)->Bool
{
if view.isHidden || !view.isUserInteractionEnabled {return false}
if view is UITextField
{
if (view as! UITextField).isEnabled {return true}
}
if view is UITextView
{
if (view as! UITextView).isEditable {return true}
}
return false
}
func TPKeyboardAvoiding_findNextInputViewAfterView(_ priorView:UIView,beneathView view:UIView, candidateView bestCandidate: inout UIView?)
{
let priorFrame = self.convert(priorView.frame, to: priorView.superview)
let candidateFrame = bestCandidate == nil ? CGRect.zero : self.convert(bestCandidate!.frame, to: bestCandidate!.superview)
var bestCandidateHeuristic = -sqrt(candidateFrame.origin.x*candidateFrame.origin.x + candidateFrame.origin.y*candidateFrame.origin.y) + ( Float(fabs(candidateFrame.minY - priorFrame.minY))<Float.ulpOfOne ? 1e6 : 0)
for childView in view.subviews
{
if TPKeyboardAvoiding_viewIsValidKeyViewCandidate(childView)
{
let frame = self.convert(childView.frame, to: view)
let heuristic = -sqrt(frame.origin.x*frame.origin.x + frame.origin.y*frame.origin.y)
+ (Float(fabs(frame.minY - priorFrame.minY)) < Float.ulpOfOne ? 1e6 : 0)
if childView != priorView && (Float(fabs(frame.minY - priorFrame.minY)) < Float.ulpOfOne
&& frame.minX > priorFrame.minX
|| frame.minY > priorFrame.minY)
&& (bestCandidate == nil || heuristic > bestCandidateHeuristic)
{
bestCandidate = childView
bestCandidateHeuristic = heuristic
}
}else
{
self.TPKeyboardAvoiding_findNextInputViewAfterView(priorView, beneathView: view, candidateView: &bestCandidate)
}
}
}
func TPKeyboardAvoiding_findNextInputViewAfterView(_ priorView:UIView,beneathView view:UIView) ->UIView?
{
var candidate:UIView?
self.TPKeyboardAvoiding_findNextInputViewAfterView(priorView, beneathView: view, candidateView: &candidate)
return candidate
}
@objc func TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView(_ obj: AnyObject)
{
func processWithView(_ view: UIView) {
for childView in view.subviews
{
if childView is UITextField || childView is UITextView
{
self.TPKeyboardAvoiding_initializeView(childView)
}else
{
self.TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView(childView)
}
}
}
if let timer = obj as? Timer, let view = timer.userInfo as? UIView {
processWithView(view)
}
else if let view = obj as? UIView {
processWithView(view)
}
}
func TPKeyboardAvoiding_initializeView(_ view:UIView)
{
if let textField = view as? UITextField,
let delegate = self as? UITextFieldDelegate, textField.returnKeyType == UIReturnKeyType.default &&
textField.delegate !== delegate
{
textField.delegate = delegate
let otherView = self.TPKeyboardAvoiding_findNextInputViewAfterView(view, beneathView: self)
textField.returnKeyType = otherView != nil ? .next : .done
}
}
func keyboardAvoidingState()->TPKeyboardAvoidingState
{
var state = objc_getAssociatedObject(self, &AssociatedKeysKeyboard.DescriptiveName) as? TPKeyboardAvoidingState
if state == nil
{
state = TPKeyboardAvoidingState()
self.state = state
}
return self.state!
}
}
// MARK: - Internal object observer
internal class TPKeyboardAvoidingState:NSObject
{
var priorInset = UIEdgeInsets.zero
var priorScrollIndicatorInsets = UIEdgeInsets.zero
var keyboardVisible = false
var keyboardRect = CGRect.zero
var priorContentSize = CGSize.zero
var priorPagingEnabled = false
}
internal extension UIScrollView
{
fileprivate struct AssociatedKeysKeyboard {
static var DescriptiveName = "KeyBoard_DescriptiveName"
}
var state:TPKeyboardAvoidingState?{
get{
let optionalObject:AnyObject? = objc_getAssociatedObject(self, &AssociatedKeysKeyboard.DescriptiveName) as AnyObject?
if let object:AnyObject = optionalObject {
return object as? TPKeyboardAvoidingState
} else {
return nil
}
}
set{
objc_setAssociatedObject(self, &AssociatedKeysKeyboard.DescriptiveName, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
Swift 4.x answer, merging answers from @Joseph Lord and @Isuru. bottomConstraint
represents the bottom constraint of the view you're interested in moving.
override func viewDidLoad() {
// Call super
super.viewDidLoad()
// Subscribe to keyboard notifications
NotificationCenter.default.addObserver(self,
selector: #selector(keyboardNotification(notification:)),
name: UIResponder.keyboardWillChangeFrameNotification,
object: nil)
}
deinit {
NotificationCenter.default.removeObserver(self)
}
@objc func keyboardNotification(notification: NSNotification) {
if let userInfo = notification.userInfo {
// Get keyboard frame
let keyboardFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
// Set new bottom constraint constant
let bottomConstraintConstant = keyboardFrame.origin.y >= UIScreen.main.bounds.size.height ? 0.0 : keyboardFrame.size.height
// Set animation properties
let duration = (userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0
let animationCurveRawNSN = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber
let animationCurveRaw = animationCurveRawNSN?.uintValue ?? UIView.AnimationOptions.curveEaseInOut.rawValue
let animationCurve = UIView.AnimationOptions(rawValue: animationCurveRaw)
// Animate the view you care about
UIView.animate(withDuration: duration, delay: 0, options: animationCurve, animations: {
self.bottomConstraint.constant = bottomConstraintConstant
self.view.layoutIfNeeded()
}, completion: nil)
}
}
The best way is using NotificationCenter to catch keyboard actions. You can follow the steps in this short article https://medium.com/@demirciy/keyboard-handling-deb1a96a8207
The following is a simple solution, whereby the text field has a constraint tying it to the bottom layout guide. It simply adds the keyboard height to the constraint's constant.
// This constraint ties the text field to the bottom layout guide
@IBOutlet var textFieldToBottomLayoutGuideConstraint: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillShow:", name:UIKeyboardWillShowNotification, object: nil);
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillHide:", name:UIKeyboardWillHideNotification, object: nil);
}
func keyboardWillShow(sender: NSNotification) {
if let keyboardSize = (sender.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
self.textFieldToBottomLayoutGuideConstraint?.constant += keyboardSize.height
}
}
func keyboardWillHide(sender: NSNotification) {
if let keyboardSize = (sender.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
self.textFieldToBottomLayoutGuideConstraint?.constant -= keyboardSize.height
}
}
I created a Swift 3 protocol to handle the keyboard appearance / disappearance
import UIKit
protocol KeyboardHandler: class {
var bottomConstraint: NSLayoutConstraint! { get set }
func keyboardWillShow(_ notification: Notification)
func keyboardWillHide(_ notification: Notification)
func startObservingKeyboardChanges()
func stopObservingKeyboardChanges()
}
extension KeyboardHandler where Self: UIViewController {
func startObservingKeyboardChanges() {
// NotificationCenter observers
NotificationCenter.default.addObserver(forName: NSNotification.Name.UIKeyboardWillShow, object: nil, queue: nil) { [weak self] notification in
self?.keyboardWillShow(notification)
}
// Deal with rotations
NotificationCenter.default.addObserver(forName: NSNotification.Name.UIKeyboardWillChangeFrame, object: nil, queue: nil) { [weak self] notification in
self?.keyboardWillShow(notification)
}
// Deal with keyboard change (emoji, numerical, etc.)
NotificationCenter.default.addObserver(forName: NSNotification.Name.UITextInputCurrentInputModeDidChange, object: nil, queue: nil) { [weak self] notification in
self?.keyboardWillShow(notification)
}
NotificationCenter.default.addObserver(forName: NSNotification.Name.UIKeyboardWillHide, object: nil, queue: nil) { [weak self] notification in
self?.keyboardWillHide(notification)
}
}
func keyboardWillShow(_ notification: Notification) {
let verticalPadding: CGFloat = 20 // Padding between the bottom of the view and the top of the keyboard
guard let value = notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue else { return }
let keyboardHeight = value.cgRectValue.height
// Here you could have more complex rules, like checking if the textField currently selected is actually covered by the keyboard, but that's out of this scope.
self.bottomConstraint.constant = keyboardHeight + verticalPadding
UIView.animate(withDuration: 0.1, animations: { () -> Void in
self.view.layoutIfNeeded()
})
}
func keyboardWillHide(_ notification: Notification) {
self.bottomConstraint.constant = 0
UIView.animate(withDuration: 0.1, animations: { () -> Void in
self.view.layoutIfNeeded()
})
}
func stopObservingKeyboardChanges() {
NotificationCenter.default.removeObserver(self)
}
}
Then, to implement it in a UIViewController, do the following:
let the viewController conform to this protocol :
class FormMailVC: UIViewControlle, KeyboardHandler {
start observing keyboard changes in viewWillAppear:
// MARK: - View controller life cycle
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
startObservingKeyboardChanges()
}
stop observing keyboard changes in viewWillDisappear:
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
stopObservingKeyboardChanges()
}
create an IBOutlet for the bottom constraint from the storyboard:
// NSLayoutConstraints
@IBOutlet weak var bottomConstraint: NSLayoutConstraint!
(I recommend having all of your UI embedded inside a "contentView", and linking to this property the bottom constraint from this contentView to the bottom layout guide)
change the constraint priority of the top constraint to 250 (low)
This is to let the whole content view slide upwards when the keyboard appears. The priority must be lower than any other constraint priority in the subviews, including content hugging priorities / content compression resistance priorities.
You may have to add a "greater than equal" constraint for this:
Here is a generic solution for all TextField Steps -
1) Create a common ViewController that is extended by other ViewControllers
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
}
@objc func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
if self.view.frame.origin.y == 0 {
self.view.frame.origin.y -= getMoveableDistance(keyboarHeight: keyboardSize.height)
}
}
}
@objc func keyboardWillHide(notification: NSNotification) {
if self.view.frame.origin.y != 0 {
self.view.frame.origin.y = 0
}
}
deinit {
NotificationCenter.default.removeObserver(self)
}
//get the distance to move up the main view for the focus textfiled
func getMoveableDistance(keyboarHeight : CGFloat) -> CGFloat{
var y:CGFloat = 0.0
if let activeTF = getSelectedTextField(){
var tfMaxY = activeTF.frame.maxY
var containerView = activeTF.superview!
while containerView.frame.maxY != self.view.frame.maxY{
let contViewFrm = containerView.convert(activeTF.frame, to: containerView.superview)
tfMaxY = tfMaxY + contViewFrm.minY
containerView = containerView.superview!
}
let keyboardMinY = self.view.frame.height - keyboarHeight
if tfMaxY > keyboardMinY{
y = (tfMaxY - keyboardMinY) + 10.0
}
}
return y
}
2) Create a extension of UIViewController and the currently active TextField
//get active text field
extension UIViewController { func getSelectedTextField() -> UITextField? {
let totalTextFields = getTextFieldsInView(view: self.view)
for textField in totalTextFields{
if textField.isFirstResponder{
return textField
}
}
return nil
}
func getTextFieldsInView(view: UIView) -> [UITextField] {
var totalTextFields = [UITextField]()
for subview in view.subviews as [UIView] {
if let textField = subview as? UITextField {
totalTextFields += [textField]
} else {
totalTextFields += getTextFieldsInView(view: subview)
}
}
return totalTextFields
}
}
Well, I think i might be too late but i found another simple version of Saqib's answer. I'm using Autolayout with constraints. I have a small view inside of another main view with username and password fields. Instead of changing the y coordinate of the view i'm saving the original constraint value in a variable and changing the constraint's constant to some value and again after the keyboard dismisses, i'm setting up the constraint to original one. This way it avoids the problem Saqib's answer has, (The view keeps on moving up and does not stop). Below is my code...
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name:UIKeyboardWillShowNotification, object: nil);
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name:UIKeyboardWillHideNotification, object: nil);
self.originalConstraint = self.centerYConstraint.constant
}
func keyboardWillShow(sender: NSNotification) {
self.centerYConstraint.constant += 30
}
func keyboardWillHide(sender: NSNotification) {
self.centerYConstraint.constant = self.originalConstraint
}
This is an improved version of @JosephLord and @Hlung's answer. It can apply whether you have tabbar or not. And it would perfectly restore view that is moved by keyboard to original position.
// You have to set this up in storyboard first!.
// It's a vertical spacing constraint between view and bottom of superview.
@IBOutlet weak var bottomSpacingConstraint: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
// Receive(Get) Notification
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardNotification:", name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardNotification:", name: UIKeyboardWillHideNotification, object: nil)
self.originalConstraint = self.keyboardHeightLayoutConstraint?.constant //for original coordinate.
}
func keyboardNotification(notification: NSNotification) {
let isShowing = notification.name == UIKeyboardWillShowNotification
var tabbarHeight: CGFloat = 0
if self.tabBarController? != nil {
tabbarHeight = self.tabBarController!.tabBar.frame.height
}
if let userInfo = notification.userInfo {
let endFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.CGRectValue()
let duration:NSTimeInterval = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0
let animationCurveRawNSN = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber
let animationCurveRaw = animationCurveRawNSN?.unsignedLongValue ?? UIViewAnimationOptions.CurveEaseInOut.rawValue
let animationCurve:UIViewAnimationOptions = UIViewAnimationOptions(rawValue: animationCurveRaw)
self.keyboardHeightLayoutConstraint?.constant = isShowing ? (endFrame!.size.height - tabbarHeight) : self.originalConstraint!
UIView.animateWithDuration(duration,
delay: NSTimeInterval(0),
options: animationCurve,
animations: { self.view.layoutIfNeeded() },
completion: nil)
}
}
You can use this library and just one line of code in appDidFinishedLaunching and u are done..
func application(application: UIApplication,didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
IQKeyboardManager.sharedManager().enable = true
return true
}
IQKeyboardManager - adjust view whenever keyboard appear link - https://github.com/hackiftekhar/IQKeyboardManager
In Swift 4.0 -
func textFieldDidBeginEditing(_ textField: UITextField) {
animateViewMoving(up: true, moveValue: 100)
}
func textFieldDidEndEditing(_ textField: UITextField) {
animateViewMoving(up: false, moveValue: 100)
}
func animateViewMoving (up:Bool, moveValue :CGFloat){
let movementDuration:TimeInterval = 0.3
let movement:CGFloat = ( up ? -moveValue : moveValue)
UIView.beginAnimations( "animateView", context: nil)
UIView.setAnimationBeginsFromCurrentState(true)
UIView.setAnimationDuration(movementDuration )
self.view.frame = self.view.frame.offsetBy(dx: 0, dy: movement)
UIView.commitAnimations()
}
Complete code for managing keyboard.
override func viewWillAppear(_ animated: Bool) {
NotificationCenter.default.addObserver(self, selector: #selector(StoryMediaVC.keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(StoryMediaVC.keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
}
override func viewWillDisappear(_ animated: Bool) {
NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil)
}
@objc func keyboardWillShow(notification: NSNotification) {
guard let userInfo = notification.userInfo else {return}
guard let keyboardSize = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else {return}
let keyboardFrame = keyboardSize.cgRectValue
if self.view.bounds.origin.y == 0{
self.view.bounds.origin.y += keyboardFrame.height
}
}
@objc func keyboardWillHide(notification: NSNotification) {
if self.view.bounds.origin.y != 0 {
self.view.bounds.origin.y = 0
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
func keyboardWillShow(_ notification:Notification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
tableView.contentInset = UIEdgeInsetsMake(0, 0, keyboardSize.height, 0)
}
}
func keyboardWillHide(_ notification:Notification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
tableView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0)
}
}
For moving your view while editing textfield try this , I have applied this ,
Option 1 :- ** **Update in Swift 5.0 and iPhone X , XR , XS and XS Max Move using NotificationCenter
Register this Notification in func viewWillAppear(_ animated: Bool)
Deregister this Notification in func viewWillDisappear(_ animated: Bool)
Note:- If you will not deregister than it will call from child class and will reason of crashing or else.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver( self, selector: #selector(keyboardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil )
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil)
}
@objc func keyboardWillShow( notification: Notification) {
if let keyboardFrame: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
var newHeight: CGFloat
let duration:TimeInterval = (notification.userInfo![UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0
let animationCurveRawNSN = notification.userInfo![UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber
let animationCurveRaw = animationCurveRawNSN?.uintValue ?? UIView.AnimationOptions.curveEaseInOut.rawValue
let animationCurve:UIView.AnimationOptions = UIView.AnimationOptions(rawValue: animationCurveRaw)
if #available(iOS 11.0, *) {
newHeight = keyboardFrame.cgRectValue.height - self.view.safeAreaInsets.bottom
} else {
newHeight = keyboardFrame.cgRectValue.height
}
let keyboardHeight = newHeight + 10 // **10 is bottom margin of View** and **this newHeight will be keyboard height**
UIView.animate(withDuration: duration,
delay: TimeInterval(0),
options: animationCurve,
animations: {
self.view.textViewBottomConstraint.constant = keyboardHeight **//Here you can manage your view constraints for animated show**
self.view.layoutIfNeeded() },
completion: nil)
}
}
Option 2 :- Its work fine
func textFieldDidBeginEditing(textField: UITextField) {
self.animateViewMoving(up: true, moveValue: 100)
}
func textFieldDidEndEditing(textField: UITextField) {
self.animateViewMoving(up: false, moveValue: 100)
}
func animateViewMoving (up:Bool, moveValue :CGFloat){
var movementDuration:NSTimeInterval = 0.3
var movement:CGFloat = ( up ? -moveValue : moveValue)
UIView.beginAnimations( "animateView", context: nil)
UIView.setAnimationBeginsFromCurrentState(true)
UIView.setAnimationDuration(movementDuration )
self.view.frame = CGRectOffset(self.view.frame, 0, movement)
UIView.commitAnimations()
}
I got this answer from this source UITextField move up when keyboard appears in Swift
IN the Swift 4 ---
func textFieldDidBeginEditing(_ textField: UITextField) {
animateViewMoving(up: true, moveValue: 100)
}
func textFieldDidEndEditing(_ textField: UITextField) {
animateViewMoving(up: false, moveValue: 100)
}
func animateViewMoving (up:Bool, moveValue :CGFloat){
let movementDuration:TimeInterval = 0.3
let movement:CGFloat = ( up ? -moveValue : moveValue)
UIView.beginAnimations( "animateView", context: nil)
UIView.setAnimationBeginsFromCurrentState(true)
UIView.setAnimationDuration(movementDuration )
self.view.frame = self.view.frame.offsetBy(dx: 0, dy: movement)
UIView.commitAnimations()
}
Very Simple and no need to code more.
Just add pod 'IQKeyboardManagerSwift'
in your podfile, and in your AppDelegate
page add code below.
import IQKeyboardManagerSwift
and in method didFinishLaunchingWithOptions()
type
IQKeyboardManager.shared.enable = true
that is it. check this video link for better understanding https://youtu.be/eOM94K1ZWN8 Hope this will help you.
None of them worked for and I ended up using content insets to move my view up when the keyboard appears.
Note: I was using a UITableView
Referenced solution @ keyboard-content-offset which was entirely written in objective C, the below solution is clean Swift.
Add the notification observer @ viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(yourClass.keyboardWillBeShown), name:UIKeyboardWillShowNotification, object: nil);
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(yourClass.keyboardWillBeHidden), name:UIKeyboardWillHideNotification, object: nil);
To get the keyboard size, we first get the userInfo dictionary from the notification object, which stores any additional objects that our receiver might use.
From that dictionary we can get the CGRect object describing the keyboard’s frame by using the key UIKeyboardFrameBeginUserInfoKey.
Apply the content inset for the table view @ keyboardWillBeShown method,
func keyboardWillBeShown(sender: NSNotification)
{
// Move the table view
if let keyboardSize = (sender.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.CGRectValue()
{
let contentInsets = UIEdgeInsetsMake(0.0, 0.0, (keyboardSize.height), 0.0);
yourTableView.contentInset = contentInsets;
yourTableView.scrollIndicatorInsets = contentInsets;
}
}
Restore the view @ keyboardWillBeHidden method
func keyboardWillBeHidden(sender: NSNotification)
{
// Moving back the table view back to the default position
yourTableView.contentInset = UIEdgeInsetsZero;
yourTableView.scrollIndicatorInsets = UIEdgeInsetsZero;
}
If you want to keep the device orientation also into consideration, use conditional statements to tailor the code to your needs.
// Portrait
UIEdgeInsetsMake(0.0, 0.0, (keyboardSize.height), 0.0);
// Landscape
UIEdgeInsetsMake(0.0, 0.0, (keyboardSize.width), 0.0);
Another solution that doesn't depend on autolayout, constraints or any outlets. What you do need is your field(s) in a scrollview.
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "makeSpaceForKeyboard:", name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "makeSpaceForKeyboard:", name: UIKeyboardWillHideNotification, object: nil)
}
func makeSpaceForKeyboard(notification: NSNotification) {
let info = notification.userInfo!
let keyboardHeight:CGFloat = (info[UIKeyboardFrameEndUserInfoKey] as! NSValue).CGRectValue().size.height
let duration:Double = info[UIKeyboardAnimationDurationUserInfoKey] as! Double
if notification.name == UIKeyboardWillShowNotification {
UIView.animateWithDuration(duration, animations: { () -> Void in
var frame = self.view.frame
frame.size.height = frame.size.height - keyboardHeight
self.view.frame = frame
})
} else {
UIView.animateWithDuration(duration, animations: { () -> Void in
var frame = self.view.frame
frame.size.height = frame.size.height + keyboardHeight
self.view.frame = frame
})
}
}
struct MoveKeyboard {
static let KEYBOARD_ANIMATION_DURATION : CGFloat = 0.3
static let MINIMUM_SCROLL_FRACTION : CGFloat = 0.2;
static let MAXIMUM_SCROLL_FRACTION : CGFloat = 0.8;
static let PORTRAIT_KEYBOARD_HEIGHT : CGFloat = 216;
static let LANDSCAPE_KEYBOARD_HEIGHT : CGFloat = 162;
}
func textFieldDidBeginEditing(textField: UITextField) {
let textFieldRect : CGRect = self.view.window!.convertRect(textField.bounds, fromView: textField)
let viewRect : CGRect = self.view.window!.convertRect(self.view.bounds, fromView: self.view)
let midline : CGFloat = textFieldRect.origin.y + 0.5 * textFieldRect.size.height
let numerator : CGFloat = midline - viewRect.origin.y - MoveKeyboard.MINIMUM_SCROLL_FRACTION * viewRect.size.height
let denominator : CGFloat = (MoveKeyboard.MAXIMUM_SCROLL_FRACTION - MoveKeyboard.MINIMUM_SCROLL_FRACTION) * viewRect.size.height
var heightFraction : CGFloat = numerator / denominator
if heightFraction < 0.0 {
heightFraction = 0.0
} else if heightFraction > 1.0 {
heightFraction = 1.0
}
let orientation : UIInterfaceOrientation = UIApplication.sharedApplication().statusBarOrientation
if (orientation == UIInterfaceOrientation.Portrait || orientation == UIInterfaceOrientation.PortraitUpsideDown) {
animateDistance = floor(MoveKeyboard.PORTRAIT_KEYBOARD_HEIGHT * heightFraction)
} else {
animateDistance = floor(MoveKeyboard.LANDSCAPE_KEYBOARD_HEIGHT * heightFraction)
}
var viewFrame : CGRect = self.view.frame
viewFrame.origin.y -= animateDistance
UIView.beginAnimations(nil, context: nil)
UIView.setAnimationBeginsFromCurrentState(true)
UIView.setAnimationDuration(NSTimeInterval(MoveKeyboard.KEYBOARD_ANIMATION_DURATION))
self.view.frame = viewFrame
UIView.commitAnimations()
}
func textFieldDidEndEditing(textField: UITextField) {
var viewFrame : CGRect = self.view.frame
viewFrame.origin.y += animateDistance
UIView.beginAnimations(nil, context: nil)
UIView.setAnimationBeginsFromCurrentState(true)
UIView.setAnimationDuration(NSTimeInterval(MoveKeyboard.KEYBOARD_ANIMATION_DURATION))
self.view.frame = viewFrame
UIView.commitAnimations()
}
And Lastly since we are using delegates methods
func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
refactored from using objective-c http://www.cocoawithlove.com/2008/10/sliding-uitextfields-around-to-avoid.html
I have done in following manner :
class SignInController: UIViewController , UITextFieldDelegate {
@IBOutlet weak var scrollView: UIScrollView!
// outlet declartion
@IBOutlet weak var signInTextView: UITextField!
var kbHeight: CGFloat!
/**
*
* @method viewDidLoad
*
*/
override func viewDidLoad() {
super.viewDidLoad()
self.signInTextView.delegate = self
}// end viewDidLoad
/**
*
* @method viewWillAppear
*
*/
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name: UIKeyboardWillHideNotification, object: nil)
}// end viewWillAppear
/**
*
* @method viewDidAppear
*
*/
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
}// end viewDidAppear
/**
*
* @method viewWillDisappear
*
*/
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
NSNotificationCenter.defaultCenter().removeObserver(self)
}
/**
*
* @method textFieldShouldReturn
* retun the keyboard value
*
*/
// MARK -
func textFieldShouldReturn(textField: UITextField) -> Bool {
signInTextView.resignFirstResponder()
return true;
}// end textFieldShouldReturn
// MARK - keyboardWillShow
func keyboardWillShow(notification: NSNotification) {
if let userInfo = notification.userInfo {
if let keyboardSize = (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
kbHeight = keyboardSize.height
self.animateTextField(true)
}
}
}// end keyboardWillShow
// MARK - keyboardWillHide
func keyboardWillHide(notification: NSNotification) {
self.animateTextField(false)
}// end keyboardWillHide
// MARK - animateTextField
func animateTextField(up: Bool) {
var movement = (up ? -kbHeight : kbHeight)
UIView.animateWithDuration(0.3, animations: {
self.view.frame = CGRectOffset(self.view.frame, 0, movement)
})
}// end animateTextField
/**
*
* @method didReceiveMemoryWarning
*
*/
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}// end didReceiveMemoryWarning
}// end SignInController
If you're like me, using Autolayout and not getting the keyboard when running the App on a simulator. This might be because Apple make you use the keyboard of you're computer as first Keyboard.
To make the keyboard from the device appear :
shift + cmd + K
Sounds stupid but I would have loved to find this answer 3 hours ago :)
A simple solution is to move view up with constant of keyboard height.
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name:UIKeyboardWillShowNotification, object: nil);
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name:UIKeyboardWillHideNotification, object: nil);
}
@objc func keyboardWillShow(sender: NSNotification) {
self.view.frame.origin.y = -150 // Move view 150 points upward
}
@objc func keyboardWillHide(sender: NSNotification) {
self.view.frame.origin.y = 0 // Move view to original position
}
Swift 5:
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(sender:)), name: UIResponder.keyboardWillShowNotification, object: nil);
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(sender:)), name: UIResponder.keyboardWillHideNotification, object: nil);
Edit: I recommend an easier and cleaner solution. Just change the class of bottom spacing constraint to KeyboardLayoutConstraint. It will automatically expand to the keyboard height.
This is an improved version of @JosephLord 's answer.
As tested on iOS 8.3 iPad Simulator, Portrait. Xcode6.3 beta4, I found his answer doesn't work when keyboard is hiding because UIKeyboardFrameEndUserInfoKey
is "NSRect: {{0, 1024}, {768, 264}}";
. The height is never 0
.
This goes back to use the traditional UIKeyboardWillShowNotification
and UIKeyboardWillHideNotification
to better tell when keyboard is hiding rather than relying on the end frame's height. UIKeyboardWillShowNotification
is also sent when keyboard frame is changed so it should cover all use cases.
// You have to set this up in storyboard first!.
// It's a vertical spacing constraint between view and bottom of superview.
@IBOutlet weak var bottomSpacingConstraint: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardNotification:"), name:UIKeyboardWillShowNotification, object: nil);
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardNotification:"), name:UIKeyboardWillHideNotification, object: nil);
}
deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
func keyboardNotification(notification: NSNotification) {
let isShowing = notification.name == UIKeyboardWillShowNotification
if let userInfo = notification.userInfo {
let endFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.CGRectValue()
let endFrameHeight = endFrame?.size.height ?? 0.0
let duration:NSTimeInterval = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0
let animationCurveRawNSN = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber
let animationCurveRaw = animationCurveRawNSN?.unsignedLongValue ?? UIViewAnimationOptions.CurveEaseInOut.rawValue
let animationCurve:UIViewAnimationOptions = UIViewAnimationOptions(rawValue: animationCurveRaw)
self.bottomSpacingConstraint?.constant = isShowing ? endFrameHeight : 0.0
UIView.animateWithDuration(duration,
delay: NSTimeInterval(0),
options: animationCurve,
animations: { self.view.layoutIfNeeded() },
completion: nil)
}
}
func registerForKeyboardNotifications(){
//Keyboard
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(keyboardWasShown), name: UIKeyboardDidShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(keyboardWillBeHidden), name: UIKeyboardDidHideNotification, object: nil)
}
func deregisterFromKeyboardNotifications(){
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillHideNotification, object: nil)
}
func keyboardWasShown(notification: NSNotification){
let userInfo: NSDictionary = notification.userInfo!
let keyboardInfoFrame = userInfo.objectForKey(UIKeyboardFrameEndUserInfoKey)?.CGRectValue()
let windowFrame:CGRect = (UIApplication.sharedApplication().keyWindow!.convertRect(self.view.frame, fromView:self.view))
let keyboardFrame = CGRectIntersection(windowFrame, keyboardInfoFrame!)
let coveredFrame = UIApplication.sharedApplication().keyWindow!.convertRect(keyboardFrame, toView:self.view)
let contentInsets = UIEdgeInsetsMake(0, 0, (coveredFrame.size.height), 0.0)
self.scrollViewInAddCase .contentInset = contentInsets;
self.scrollViewInAddCase.scrollIndicatorInsets = contentInsets;
self.scrollViewInAddCase.contentSize = CGSizeMake((self.scrollViewInAddCase.contentSize.width), (self.scrollViewInAddCase.contentSize.height))
}
/**
this method will fire when keyboard was hidden
- parameter notification: contains keyboard details
*/
func keyboardWillBeHidden (notification: NSNotification) {
self.scrollViewInAddCase.contentInset = UIEdgeInsetsZero
self.scrollViewInAddCase.scrollIndicatorInsets = UIEdgeInsetsZero
}
A Swift 5
solution for frédéric-adda :
protocol KeyboardHandler: class {
var bottomConstraint: NSLayoutConstraint! { get set }
func keyboardWillShow(_ notification: Notification)
func keyboardWillHide(_ notification: Notification)
func startObservingKeyboardChanges()
func stopObservingKeyboardChanges()
}
extension KeyboardHandler where Self: UIViewController {
func startObservingKeyboardChanges() {
// NotificationCenter observers
NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillShowNotification, object: nil, queue: nil) { [weak self] notification in
self?.keyboardWillShow(notification)
}
// Deal with rotations
NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillChangeFrameNotification, object: nil, queue: nil) { [weak self] notification in
self?.keyboardWillShow(notification)
}
// Deal with keyboard change (emoji, numerical, etc.)
NotificationCenter.default.addObserver(forName: UITextInputMode.currentInputModeDidChangeNotification, object: nil, queue: nil) { [weak self] notification in
self?.keyboardWillShow(notification)
}
NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillHideNotification, object: nil, queue: nil) { [weak self] notification in
self?.keyboardWillHide(notification)
}
}
func keyboardWillShow(_ notification: Notification) {
let verticalPadding: CGFloat = 20 // Padding between the bottom of the view and the top of the keyboard
guard let value = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else { return }
let keyboardHeight = value.cgRectValue.height
// Here you could have more complex rules, like checking if the textField currently selected is actually covered by the keyboard, but that's out of this scope.
self.bottomConstraint.constant = keyboardHeight + verticalPadding
UIView.animate(withDuration: 0.1, animations: { () -> Void in
self.view.layoutIfNeeded()
})
}
func keyboardWillHide(_ notification: Notification) {
self.bottomConstraint.constant = 0
UIView.animate(withDuration: 0.1, animations: { () -> Void in
self.view.layoutIfNeeded()
})
}
func stopObservingKeyboardChanges() {
NotificationCenter.default.removeObserver(self)
}
}
In any UIViewController
:
KeyboardHandler
protocolextension AnyViewController: KeyboardHandler {}
@IBOutlet var bottomConstraint: NSLayoutConstraint!
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
startObservingKeyboardChanges()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
stopObservingKeyboardChanges()
}
Enjoy!
If you are like me who has tried all the above solutions and still your problem is not solved, I have a got a great solution for you that works like a charm. First I want clarify few things about some of solutions mentioned above.
i am working with swift 4 and i am solved this issue without use any extra bottom constraint look my code is here.its really working on my case
1) Add Notification Observer in did load
override func viewDidLoad() { super.viewDidLoad() setupManager() // Do any additional setup after loading the view. NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil) }
2) Remove Notification Observer like
deinit { NotificationCenter.default.removeObserver(self) }
3) Add keyboard show/ hide methods like
@objc func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
UIView.animate(withDuration: 0.1, animations: { () -> Void in
self.view.frame.origin.y -= keyboardSize.height
self.view.layoutIfNeeded()
})
}
}
@objc func keyboardWillHide(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
UIView.animate(withDuration: 0.1, animations: { () -> Void in
self.view.frame.origin.y += keyboardSize.height
self.view.layoutIfNeeded()
})
}
}
4) Add textfeild delegate and add touchesBegan methods .usefull for hide the keyboard when touch outside the textfeild on screen
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
view.endEditing(true)
}
I love clean Swift code. So here's the tightest code I could come up with to move a text view up/down with the keyboard. It's currently working in an iOS8/9 Swift 2 production app.
UPDATE (March 2016): I just tightened up my previous code as much as possible. Also, there are a bunch of popular answers here that hardcode the keyboard height and animation parameters. There's no need for that, not to mention that the numbers in these answers don't always line up with the actual values I'm seeing on my 6s+ iOS9 (keyboard height of 226, duration of 0.25, and animation curve of 7). In any case, it's almost no extra code to get those values straight from the system. See below.
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "animateWithKeyboard:", name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "animateWithKeyboard:", name: UIKeyboardWillHideNotification, object: nil)
}
func animateWithKeyboard(notification: NSNotification) {
// Based on both Apple's docs and personal experience,
// I assume userInfo and its documented keys are available.
// If you'd like, you can remove the forced unwrapping and add your own default values.
let userInfo = notification.userInfo!
let keyboardHeight = (userInfo[UIKeyboardFrameEndUserInfoKey] as! NSValue).CGRectValue().height
let duration = userInfo[UIKeyboardAnimationDurationUserInfoKey] as! Double
let curve = userInfo[UIKeyboardAnimationCurveUserInfoKey] as! UInt
let moveUp = (notification.name == UIKeyboardWillShowNotification)
// baseContraint is your Auto Layout constraint that pins the
// text view to the bottom of the superview.
baseConstraint.constant = moveUp ? -keyboardHeight : 0
let options = UIViewAnimationOptions(rawValue: curve << 16)
UIView.animateWithDuration(duration, delay: 0, options: options,
animations: {
self.view.layoutIfNeeded()
},
completion: nil
)
}
NOTE: This code covers the most comment/general case. However, more code may be needed to handle different orientations and/or custom keyboards Here's an in-depth article on working with the iOS keyboard. If you need to handle every scenario, this may help.
For anyone not using storyboards to set the layout constraint. This is a pure programmatic way to get it working on Swift 5:
var discussionsMessageBoxBottomAnchor: NSLayoutConstraint = NSLayoutConstraint()
discussionsMessageBox
. Also, add the listener for keyboard events.For correctly initializing the constraint, you need to first add the subview and then define the constraint.
NotificationCenter.default.addObserver(self,
selector: #selector(self.keyboardNotification(notification:)),
name: NSNotification.Name.UIKeyboardWillChangeFrame,
object: nil)
view.addSubview(discussionsMessageBox)
if #available(iOS 11.0, *) {
discussionsMessageBoxBottomAnchor = discussionsMessageBox.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: 0)
} else {
// Fallback on earlier versions
discussionsMessageBoxBottomAnchor = discussionsMessageBox.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0)
}
NSLayoutConstraint.activate([ discussionsMessageBoxBottomAnchor ])
deinit
.deinit {
NotificationCenter.default.removeObserver(self)
}
extension DiscussionsViewController {
@objc func keyboardNotification(notification: NSNotification) {
guard let userInfo = notification.userInfo else { return }
let endFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
let endFrameY = endFrame?.origin.y ?? 0
let duration:TimeInterval = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0
let animationCurveRawNSN = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber
let animationCurveRaw = animationCurveRawNSN?.uintValue ?? UIView.AnimationOptions.curveEaseInOut.rawValue
let animationCurve:UIView.AnimationOptions = UIView.AnimationOptions(rawValue: animationCurveRaw)
if endFrameY >= UIScreen.main.bounds.size.height {
self.discussionsMessageBoxBottomAnchor.constant = 0.0
} else {
//Changed line
self.discussionsMessageBoxBottomAnchor.constant = -1 * (endFrame?.size.height ?? 0.0)
}
UIView.animate(
withDuration: duration,
delay: TimeInterval(0),
options: animationCurve,
animations: { self.view.layoutIfNeeded() },
completion: nil)
}
}
I modified @Simpa solution a little bit.........
override func viewDidLoad()
{
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("makeSpaceForKeyboard:"), name:UIKeyboardWillShowNotification, object: nil);
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("makeSpaceForKeyboard:"), name:UIKeyboardWillHideNotification, object: nil);
}
deinit{
NSNotificationCenter.defaultCenter().removeObserver(self)
}
var keyboardIsVisible = false
override func makeSpaceForKeyboard(notification: NSNotification) {
let info = notification.userInfo!
let keyboardHeight:CGFloat = (info[UIKeyboardFrameEndUserInfoKey] as! NSValue).CGRectValue().size.height
let duration:Double = info[UIKeyboardAnimationDurationUserInfoKey] as! Double
if notification.name == UIKeyboardWillShowNotification && keyboardIsVisible == false{
keyboardIsVisible = true
UIView.animateWithDuration(duration, animations: { () -> Void in
var frame = self.view.frame
frame.size.height = frame.size.height - keyboardHeight
self.view.frame = frame
})
} else if keyboardIsVisible == true && notification.name == UIKeyboardWillShowNotification{
}else {
keyboardIsVisible = false
UIView.animateWithDuration(duration, animations: { () -> Void in
var frame = self.view.frame
frame.size.height = frame.size.height + keyboardHeight
self.view.frame = frame
})
}
}
Here is my version for a solution for Swift 2.2:
First register for Keyboard Show/Hide Notifications
NSNotificationCenter.defaultCenter().addObserver(self,
selector: #selector(MessageThreadVC.keyboardWillShow(_:)),
name: UIKeyboardWillShowNotification,
object: nil)
NSNotificationCenter.defaultCenter().addObserver(self,
selector: #selector(MessageThreadVC.keyboardWillHide(_:)),
name: UIKeyboardWillHideNotification,
object: nil)
Then in methods coresponding for those notifications move the main view up or down
func keyboardWillShow(sender: NSNotification) {
if let keyboardSize = (sender.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.CGRectValue() {
self.view.frame.origin.y = -keyboardSize.height
}
}
func keyboardWillHide(sender: NSNotification) {
self.view.frame.origin.y = 0
}
The trick is in the "keyboardWillShow" part which get calls every time "QuickType Suggestion Bar" is expanded or collapsed. Then we always set the y coordinate of the main view which equals the negative value of total keyboard height (with or without the "QuickType bar" portion).
At the end do not forget to remove observers
deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
Source: Stackoverflow.com