8

In a UIViewController I have several text fields and a button is on the bottom of the UIViewController. For the button, I have set a bottom constraint with a constant of 0. Then I made an outlet from the bottom constraint to the UIViewController.

When I run my code, the button does not move upwards. I have seen suggestions on stackoverflow that I should add UIScrollView, but that means, I would have to delete all the objects on the UIViewController, put the UIScrollView and then put my objects on the UIVIewController again.

@IBOutlet weak var bottomConstraint: NSLayoutConstraint!

// When tapping outside of the keyboard, close the keyboard down
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
    self.view.endEditing(true)
}

// Stop Editing on Return Key Tap. textField parameter refers to any textfield within the view
func textFieldShouldReturn(textField: UITextField) -> Bool {
    textField.resignFirstResponder()
    return true
}

// When keyboard is about to show assign the height of the keyboard to bottomConstraint.constant of our button so that it will move up
func keyboardWillShow(notification: NSNotification) {
    if let userInfo = notification.userInfo {
        if let keyboardSize: CGRect = (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
            bottomConstraint.constant = keyboardSize.size.height
            view.setNeedsLayout()
        }
    }
}

// When keyboard is hidden, move the button to the bottom of the view
func keyboardWillHide(notification: NSNotification) {
    bottomConstraint.constant = 0.0
    view.setNeedsLayout()
}

enter image description here

fhe
  • 5,977
  • 1
  • 40
  • 43
bibscy
  • 2,346
  • 3
  • 26
  • 70

4 Answers4

8

The typical way to address this would be to move the keyboard with code like this:

in ViewController class:

  func keyboardWillShow(notification: NSNotification) {

        if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
            if view.frame.origin.y == 0{
                let height = keyboardSize.height

                self.view.frame.origin.y += height
            }

        }

    }

    func keyboardWillHide(notification: NSNotification) {
        if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
            if view.frame.origin.y != 0 {
                let height = keyboardSize.height
                self.view.frame.origin.y -= height
            }

        }
    }

in ViewDidLoad method:

  NotificationCenter.default.addObserver(self, selector: Selector("keyboardWillShow:"), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
  NotificationCenter.default.addObserver(self, selector: Selector("keyboardWillHide:"), name: NSNotification.Name.UIKeyboardWillHide, object: nil)

Please Read This: The way you are trying to solve your problem is not allowed. In the code above, if you change view to your button variable name, the button will shoot up and then fall back down. This is because Auto Layout and Programmatic layout do not work together, it is one or the other. The way you fix this is by programmatically creating that button (with CGRect), then using the code above to move only that button on keyboard press. (Do that by changing view to your button variable name.

   func keyboardWillShow(notification: NSNotification) {

    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
        if view.frame.origin.y == 0{
            let height = keyboardSize.height
            self.yourBtn.frame.origin.y += height
        }
    }
}

func keyboardWillHide(notification: NSNotification) {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
        if view.frame.origin.y != 0 {
            let height = keyboardSize.height
            self.yourBtn.frame.origin.y -= height
        }
    }
}

To programmatically create the button you would use code similar to this:

myButton.frame = CGRect(...)
John Warlow
  • 2,872
  • 1
  • 34
  • 47
Ryan Cocuzzo
  • 2,829
  • 5
  • 32
  • 53
  • Could you please tell me why are you saying that the way I am doing it is weird? Do you think this is exposing my code to certain bugs? – bibscy Sep 01 '16 at 02:00
  • @bibscy I figured it out. Check out my updated answer. And to be honest I do believe this will be exposed to layout bugs (particularly on the iPad layout or smaller iPhones) when the text fields themselves take up more than half the screen and the button goes directly over them. To solve that, I would embed your text fields in a scroll view. – Ryan Cocuzzo Sep 01 '16 at 13:46
  • I also switched the `keyboardWillShow` and `keyboardWillHide` implementations before on accident, but I fixed that now so it should not make things disappear anymore. – Ryan Cocuzzo Sep 01 '16 at 13:47
  • I have emebedded my text filed in a scroll View, added the button programmtically. 1. The button does not lift up with the keyboard when I start editing the textfield. 2. when I click outside of the keyboard, the keyboard does not resign. (it did before adding the scroll View) – bibscy Sep 01 '16 at 18:12
  • I tried what you suggested but the button stays under the scroll View. Please see my new questions http://stackoverflow.com/questions/42543593 . Many thanks – bibscy Mar 02 '17 at 23:43
3

You need add(viewDidLoad) observers to call your functions:

NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(keyboardWillShow), name: UIKeyboardDidShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(keyboardWillHide), name: UIKeyboardDidHideNotification, object: nil)
Klevison
  • 3,252
  • 1
  • 17
  • 28
  • I added the observers and it worked. However, @Ryan said that my implementation will be exposed to unexpected behaviour because of different iphone screen sizes. He suggested a method, but it did not work. The button does not lift with the keyboard, the keyboard no longer resigns when tapping outside of it, when tapping outside of keyboard, the programmatic button i have now created no longer shows, On iphone 6 the button is pinned to the bottom of the screen, but it looks different on other screens, ` nextButton.frame = CGRectMake(0, 620, 375, 50)`. Can you help? – bibscy Sep 01 '16 at 18:43
3

Complimentary to Ryan's answer above, this can be done all with auto-layout and no need for frames and CGRect.

Swift 5

In your view, constrain your button as you normally would but add a reference to the constraint for modification when the keyboard hides/shows:

var bottomButtonConstraint = NSLayoutConstraint()

bottomButtonConstraint = yourButton.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor, constant: -12)
bottomButtonConstraint.isActive = true

In your ViewController's viewDidLoad():

NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)

NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)

Also in your ViewController:

@objc private func keyboardWillShow(notification: NSNotification) {
    if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
        self.yourCustomView.bottomButtonConstraint.constant -= keyboardSize.height
    }
}

@objc private func keyboardWillHide(notification: NSNotification) {
    self.yourCustomView.bottomButtonConstraint.constant = -12
}
Justin Vallely
  • 5,525
  • 3
  • 27
  • 40
0

Consider using this pod: https://cocoapods.org/pods/IQKeyboardManager In AppDelegate.swift, just import IQKeyboardManagerSwift framework and enable IQKeyboardManager.

import IQKeyboardManagerSwift

@UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate {

var window: UIWindow?

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

  IQKeyboardManager.shared.enable = true

  return true
}

}

Nedim Karavdic
  • 157
  • 1
  • 8
  • IQKeyboardManager does update the text field if the text field is hidden by the keyboard. But it doesn't answer the question. – Ankur Lahiry Feb 22 '22 at 11:43