0
votes

Despite all the "Dismissing Keyboard" questions, there doesn't appear to be one referring to this specific scenario.

I am working on a ViewController with the following (simplified) hierarchy:

  • UIView
    • UIScrollView
      • UIStackView
        • PLCustomViewOne
          • UIButton
          • UITextField
        • PLCustomViewTwo
          • UIButton
          • UITextField

The ViewController is loaded from an XIB as are the two PLCustomView's. I can't seem to get the view to endEditing (dismiss the keyboard) when the user selects anything contained by the UIStackViews.

Adding a gesture recognizer to the view certainly will work; however, this requires that every view in the hierarchy gets the gesture recognizer added to it, which doesn't seem very scalable. There are actually quite a few subviews on the real version of this.

I have tried adding the dismissal in touchesBegan to the UIView subclass of each of the custom views, but this only dismisses the keyboard if the user touches the view which is containing the firstResponder. For example, if the textField in PLCustomViewOne is active then touching anywhere else in that view dismisses the keyboard, but touching outside it, say, PLCustomViewTwo, does not.

2
Did you try [self.scrollvoew endEditing];S. Karthik
Are you suggesting adding scrollView.endEditing(true) in the top-level UIView? The issue is that on that top-level UIView, touchesBegan isn't being called.Michael Voccola
@MichaelVoccola Please give feedvack or additional info if the answer below did not solve your problem. If it did, please close the question.dfrib

2 Answers

1
votes

In your view controller class, create outlets to your two custom views (via ctrl-drag from storyboard)

@IBOutlet weak var customViewOne: PLCustomViewOne!
@IBOutlet weak var customViewTwo: PLCustomViewTwo!

From these, you should be able to access your UITextField instances, say by

customViewOne.textField
customViewTwo.textField

(Preferably if you make this property non-private in your custom class). Also (in you view controller class) create a private UITextField property to hold the (possibly) current (/most recently used) text field.

private var currentTextField: UITextField?

Overload the viewDidLoad method of your UIViewController subclass to initialize the delegates for the two "public" text field instances

override func viewDidLoad() {
    super.viewDidLoad()

    // Handle the user input in the text fields through delegate callbacks (and set tags)
    customViewOne.textField.delegate = self
    customViewOne.textField.delegate = self
}

Moreover, use the two textFieldShouldReturn(...) and textFieldDidBeginEditing(...) methods of the UITextFieldDelegate to resign the (active) text fields status's as first responder and to update the currentTextField reference, respectively.

// UITextFieldDelegate
func textFieldShouldReturn(textField: UITextField) -> Bool {
    // User finished typing (hit return): hide the keyboard.
    textField.resignFirstResponder()
    return true
}

func textFieldDidBeginEditing(textField: UITextField) {
    currentTextField = textField
}

Finally (also in view controller class), resign any possibly current text field as first responder, in case any of your two buttons is pressed in-middle-of editing/or when touchesBegan in any of the UIView

... user does action/tap/gesture elsewhere {
    if let currentTextField = currentTextField {
        currentTextField.resignFirstResponder()
    }
}

The key here is that you keep track of which text field is first responder (by currentTextField property in view controller), and resign just this text field.

0
votes

I was able to achieve the desired result by adding the following to the custom views:

private func addTapToResignFirstResponder () {
    let action = UITapGestureRecognizer(target: self, action: Selector("dismissFirstResponder"))
    self.view.addGestureRecognizer(action)
}

func dismissFirstResponder () {
    UIApplication.sharedApplication().sendAction("resignFirstResponder", to:nil, from:nil, forEvent:nil)
}

where addTapToResignFirstResponder() is called in the UIView's initialization.

This globally dismisses the first responder regardless of where it is without needing a reference.

dismissFirstResponder() can also be overridden in subclasses for additional flexibility.