0

This is my view controller.

enter image description here

I am trying to move the textFields up while the keyboard is covering textFields of the bottom of the screen. Here is the code I've done :

    override func viewDidLoad() {
        super.viewDidLoad()
        
        txtFName.delegate = self
        txtLName.delegate = self
        txtCompany.delegate = self
        txtStreet1.delegate = self
        txtStreet2.delegate = self
        txtTown.delegate = self
        txtPin.delegate = self
        txtPhone.delegate = self
        txtEmail.delegate = self
        
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(self.keyBoardWillChange(notification:)),
            name: UIResponder.keyboardWillShowNotification, object: nil)
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(self.keyBoardWillChange(notification:)),
            name: UIResponder.keyboardWillHideNotification, object: nil)
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(self.keyBoardWillChange(notification:)),
            name: UIResponder.keyboardWillChangeFrameNotification, object: nil)

    deinit {
        NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil)
        NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil)
        NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
    }

    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        print("Return Tapped")
        txtFName.resignFirstResponder()
        txtLName.resignFirstResponder()
        txtCompany.resignFirstResponder()
        txtStreet1.resignFirstResponder()
        txtStreet2.resignFirstResponder()
        txtTown.resignFirstResponder()
        txtPin.resignFirstResponder()
        txtPhone.resignFirstResponder()
        txtEmail.resignFirstResponder()
        view.frame.origin.y = 0
        return true
    }
    
    @objc func keyBoardWillChange(notification: Notification) {
        print("Keyboard will show: \(notification.name.rawValue)")
        view.frame.origin.y = -250
    }

Now while I am tapping on any of the textFields, the whole view is moving up. "txtFName", "txtLName".. these textFields are not being visible.

I want to move up the view only when I would tap on "txtPin", "txtPhone", "txtEmail". Rest textfields would remain in the default position even when the keyboard appears.

what the required changes are?

James Z
  • 12,209
  • 10
  • 24
  • 44
Ahana
  • 21
  • 4
  • Does this answer your question? [How can I make a UITextField move up when the keyboard is present - on starting to edit?](https://stackoverflow.com/questions/1126726/how-can-i-make-a-uitextfield-move-up-when-the-keyboard-is-present-on-starting) – Laffen Oct 22 '21 at 11:39
  • No. But it's been solved in some other way. Thanks. – Ahana Oct 23 '21 at 06:28
  • if the question is solved, you should either close the question or add an answer so that future readers may use the same solution as you. – Laffen Oct 23 '21 at 19:08

1 Answers1

0

Generally you can use your isFirstResponder property on your text field. When true, it means that this is the text field you wish to focus on and move upwards when it starts being edited.

@objc private func keyBoardWillChange(notification: Notification) {
    let possibleTextFields: [UITextField] = [txtLName, txtCompany, txtStreet1, txtStreet2, txtTown, txtPin, txtPhone, txtEmail]
    let focusedTextField: UITextField? = possibleTextFields.first { $0.isFirstResponder }
    ...
}

Now that you have that you would still need to calculate the offset that is needed to move your view

Getting the frame of keyboard:

guard let info = notification.userInfo else { return }
guard let value: NSValue = info[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else { return }
let newFrame = value.cgRectValue

Computing the difference

There are tools to check frames which allow you to convert frames to different coordinate systems (views)

private func getVerticalDifference(keyboardFrame: CGRect, viewToFocus: UIView, panel: UIView) -> CGFloat {
    let keyboardInPanel = panel.convert(keyboardFrame, from: nil)
    let viewInPanel = panel.convert(viewToFocus.bounds, from: viewToFocus)
    
    return viewInPanel.maxY - keyboardInPanel.minY
}

I suggest you use the view of your view controller for your panel parameter it should be something that is not being changed (Which is not the case in your current code. Avoid changing frame of the view of your view controller).

Applying the difference:

To apply the difference I suggest that you use constraints. Put all your text fields on a single "panel" view. This view may best also put into a scroll view so user may scroll through it on smaller devices. Now panel (or scroll view) can have a low priority (500 for instance) bottom constraint to view controller. Then another high priority bottom constraint set to greaterThan meaning that bottom will be fixed to "greater than X" where X can be setup later.

Now you can drag an outlet to your code from this greaterThan constraint. And then all you need to do is

bottomConstraintOutlet.constant = max(0.0, getVerticalDifference(...))
Matic Oblak
  • 16,318
  • 3
  • 24
  • 43