Finish editing UITextField on back button tap

2020-03-25 15:59发布

I have 2 controllers inside NavigationController. First pushes the second one to the stack and user can interact with the text field there. Then (in one scenario) user will tap on back button to be taken to the previous screen. Assuming that loading of second one is 'heavy', so I will be keeping only one instance of it once it is needed.

Expected: I would like to have keyboard hidden once back button is pressed.

Actual: First responder keeps being restored when I go back to the second for the second time. How to prevent that? Resigning first responder also doesn't do the trick there...

Problem demo: https://gitlab.com/matrejek/TestApp

Major code parts:

class FirstViewController: UIViewController {
    var child: UIViewController = {
        let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
        let vc = storyboard.instantiateViewController(withIdentifier: "child")
        return vc
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    @IBAction func onButtonTap(_ sender: Any) {
        self.navigationController?.pushViewController(child, animated: true)
    }
}

class SecondViewController: UIViewController {
    @IBOutlet weak var textField: UITextField!

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        view.endEditing(true)
    }
}

1条回答
Animai°情兽
2楼-- · 2020-03-25 16:29

This does seem odd --- and it seems like your approach should work.

Apparently (based on quick testing), since you are not allowing the Navigation Controller to release the SecondVC, the text field is remaining "active."

If you add this to SecondViewController, it will prevent the keyboard from "auto re-showing" the next time you navigate to the controller - not sure it will be suitable for you, but it will do the job:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    DispatchQueue.main.async {
        self.view.endEditing(true)
    }
}

Edit: Jan 25 2020

Based on new comments, yes, this seems to be a bug.

My previous work-around answer worked -- sort of. The result was the keyboard popping up and then disappearing on subsequent pushes of child.

Following is a better work-around. We have SecondViewController conform to UITextFieldDelegate and add a BOOL class var / property that will prevent the text field from becoming first responder. Comments should be clear:

class FirstViewController: UIViewController {
    var child: UIViewController = {
        let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
        let vc = storyboard.instantiateViewController(withIdentifier: "child")
        return vc
    }()

    @IBAction func onButtonTap(_ sender: Any) {
        self.navigationController?.pushViewController(child, animated: true)
    }
}

// conform to UITextFieldDelegate
class SecondViewController: UIViewController, UITextFieldDelegate {
    @IBOutlet weak var textField: UITextField!

    // bool var to prevent text field re-becoming first responder
    // when VC is pushed a second time
    var bResist: Bool = false

    override func viewDidLoad() {
        super.viewDidLoad()

        // assign text field delegate
        textField.delegate = self
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        // view has appeared, so allow text field to become first responder
        bResist = false
    }

    func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
        return !bResist
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)

        // end editing on this view
        view.endEditing(true)

        // we want to resist becoming first responder on next push
        bResist = true
    }
}
查看更多
登录 后发表回答