0

I am trying to move UITextFields when the Keyboard shows. Now I've seen videos and read articles on how to do it. I haven't seen one that uses the textfield itself, rather they use the bottom constraint of the textfield. Here is a video of what my code does, below is my code.

class ViewController: UIViewController, UITextFieldDelegate {

@IBOutlet weak var nameTF: UITextField!
@IBOutlet weak var emailTF: UITextField!

var selectedTextField: UITextField?

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    createKeyboardNotification()
}

func createKeyboardNotification() {
    NotificationCenter.default.addObserver(self, selector: #selector(respondToKeyboardWillShow(notification:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(respondToKeyboardWillHide(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}

@objc func respondToKeyboardWillShow(notification: Notification) {
    adjustHeightForTextFields(isKeyboardHidden: false, notification: notification, textField: selectedTextField)
}

@objc func respondToKeyboardWillHide(notification: Notification) {
   adjustHeightForTextFields(isKeyboardHidden: true, notification: notification, textField: selectedTextField)
}


func adjustHeightForTextFields(isKeyboardHidden: Bool, notification: Notification, textField: UITextField?) {

    guard let userInfo = notification.userInfo else { return }
    let keyboardFrameRect = userInfo[UIKeyboardFrameEndUserInfoKey] as! CGRect

    if let textField = textField {
        let textFieldYPosition = textField.frame.origin.y

        if view.frame.maxY - textFieldYPosition > keyboardFrameRect.height {
            UIView.animate(withDuration: 0.25) {
                textField.frame.origin.y = (self.view.frame.maxY - textField.frame.size.height - keyboardFrameRect.height - 8)
            }
        }
        else {
            UIView.animate(withDuration: 0.25) {
                let difference = textFieldYPosition - keyboardFrameRect.height
                textField.frame.origin.y = difference + 16 + self.view.safeAreaInsets.bottom - 8
            }
        }
    }
}

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    self.view.endEditing(true)
}

func textFieldDidBeginEditing(_ textField: UITextField) {
    switch textField {
    case nameTF:
        print("NAME")
        selectedTextField = nameTF
        break
    case emailTF:
        print("EMAIL")
        selectedTextField = emailTF
        break
    default:
        break
    }
}
}

If you have seen the video, I've come across some strange things. First when you tap on the textfield it works like its suppose to, but when you start typing the textfield just disappears. I didn't come across this when I was using the textfield bottom constraint. Now the second part is when the keyboard is already shown the textfield doesn't animate correctly, until you click it twice.

Now I am not using a scrollview, but would like to push the whole content or would I need to use a scrollview. If you take a look at this video, you can better understand what I mean by wanting to push the content.

Would really appreciate any help provided, Thanks. :)

Luis F Ramirez
  • 168
  • 2
  • 11
  • I'd highly recommend that you redesign your view and use constraint based layouts. Pushing pixels is much harder to do given todays variety of screens, rotations, etc. – drekka Jul 19 '18 at 04:27
  • instead moving textfield you can resize viewcontroller's view or containerview – SPatel Jul 19 '18 at 04:33
  • Simply use IQKeyboardManager. https://github.com/hackiftekhar/IQKeyboardManager – Naresh Jul 19 '18 at 04:45
  • Possible duplicate of [How can I make a UITextField move up when the keyboard is present?](https://stackoverflow.com/questions/1126726/how-can-i-make-a-uitextfield-move-up-when-the-keyboard-is-present) – Prashant Tukadiya Jul 19 '18 at 04:51
  • The problem should be lie in `selectedTextField `, it will have race condition and might lead to wrong textfield – Tj3n Jul 19 '18 at 06:00
  • Are you sure you are not doing anything in `shouldChangeCharactersIn` of `UITextFieldDelegate`? Also, I would recommend putting `super.touchesBegan(touches, with: event)` in ViewController's `touchesBegan()` method. – Sunil Chauhan Jul 19 '18 at 07:17

2 Answers2

0

You need to move the view up only when the textField on the bottom becomes active.

//Create a global variable to use as our keyboardHeight
var keyboardHeight: CGFloat = 0.0

override func viewDidLoad() {
        super.viewDidLoad()

    //Set the delegate only for the emailTF
    emailTF.delegate = self

    //Set up an observer. This will help us calculate keyboard height dynamically, depending on the iPhone the app runs on.
    NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
}

@objc private func keyboardWillShow(notification: NSNotification) {
    if let keyboardRectValue = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
        keyboardHeight = keyboardRectValue.height
    }
}

Then move the view up and down when the textField becomes active/inactive.

func textFieldDidBeginEditing(_ textField: UITextField) {
        print("MOVE VIEW UP")
        if self.view.frame.origin.y == 0{
            self.view.frame.origin.y -= keyboardHeight
        }
}

func textFieldDidEndEditing(_ textField: UITextField) {
        print("MOVE VIEW DOWN")
        if self.view.frame.origin.y != 0{
            self.view.frame.origin.y += keyboardHeight
        }
}

func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        print("MOVE VIEW DOWN")
        if self.view.frame.origin.y != 0{
            self.view.frame.origin.y += keyboardHeight
        }
    return true
}

This should definitely work. Good luck!

Starsky
  • 1,829
  • 18
  • 25
  • Remove all the other code you have, except the IBOutlets and use only the one I provided. – Starsky Jul 19 '18 at 08:18
  • I tied it and it didn't work. I did exactly as you said. Also I notice that the keyboard height will be 0, when the textFieldDidBeginEditing because it gets called before the observer. – Luis F Ramirez Jul 19 '18 at 15:54
  • No, the observer fires first, then the textFieldDidBeginEditing. Can you show all your viewController with the new code I provided above? From import UIKit till the last bracket? – Starsky Jul 20 '18 at 13:03
0

I agree that it's not clear always. We do this in our app and we used a scroll view. We embed the entire page in the scroll view. Then we move the bottom of the scroll view up. Scrollviews are easy to implement.

@objc func keyboardWillShow(notification: NSNotification) {
    // Only deal with this if the window is active and visible.
    if !self.isViewLoaded || self.view.window == nil {
        return
    }
    if let userInfo = notification.userInfo {
        let keyboardFrame = userInfo[UIKeyboardFrameEndUserInfoKey] as! CGRect
        scrollView.frame = CGRect(x: view.frame.origin.x,y: view.frame.origin.y,width: view.frame.width, height: view.frame.height - keyboardFrame.height - 64)

     }
    self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard)))
}

And when the keyboard disappears:

@objc func keyboardWillHide(notification: NSNotification) {
    scrollView.frame = scrollViewOriginalFrame
}
David J
  • 1,018
  • 1
  • 9
  • 14
  • Also, the UIScrollView lets you set the focus on which field so you can choose which is visible with the scrollRectToVisible function. – David J Jul 28 '18 at 11:25