iosswiftuinavigationcontrolleriphone-softkeyboard

Unwanted Keyboard after Back Navigation


All UIViewControllers in my app are managed by a top level UINavigationController. In the UIViewController that is currently on top of my navigation stack I have a set of UITextFields.

A problem occurs when I call becomeFirstResponder() on one of these text fields and then immediately navigate back without changing focus first, e.g. by tapping on another field. After navigating back one level, the keyboard appears, and I have found no way to keep it from appearing or making it disappear. It even stays as I further push views of the navigation stack.

  1. This is directly or indirectly connected to the becomeFirstResponder() call, because without that call, the problem does not occur.
  2. Even if I, for test purposes, call resignFirstResponder() immediately after becomeFirstResponder() the keyboard still appears after navigating back.

I have tried other ways like detecting and resigning the first responder or calling endEditing() in viewWillDisappear() but did not succeed. I’m not even sure what this keyboard belongs to after the corresponding view is popped off the stack. I cannot inspect the keyboard in the View Debugger as it does not appear there.

Why does the keyboard appear, and how can I prevent it?


Solution

  • It turns out that the form validation that was grabbing the first responder kept reclaiming it until the field content was valid. If it does not release the status before back navigation the keyboard stays and it becomes difficult to assign first responder to another control.

    Solution in my case was to more carefully keep track of which field is first responder, detect the back button push, allow resigning first responder unconditionally in that case, and then resign first responder for that field.

    var currentTextField: UITextField?
    
    override func viewWillDisappear(animated: Bool) {
        super.viewWillDisappear(animated)
        if let currentField = self.currentTextField {
            currentField.resignFirstResponder()
        }
    }
    
    override public func willMoveToParentViewController(parent: UIViewController?) {
        if (parent == nil) {
            backButtonPushed = true
        }
        super.willMoveToParentViewController(parent)
    }
    
    func customTextFieldDidBeginEditing(textField: UITextField) {
        currentTextField = textField
    }
    
    public func textFieldShouldEndEditing(textField: UITextField) -> Bool {
    
        // ...
        // Must return true if back button is pushed.
        if backButtonPushed {
            return true
        } else {
            // ...
        }
    }