Sometimes, we want some data to be collected by a subordinate view to be returned to a superordinate view in a view hierarchy. Let’s look at this by means of an example.
Setting up the child/source view
Let’s place a data entry field (like a text field) in a child view and connect it to an outlet in its controller called userText. We can define a function in that controller to interrogate the outlet and return information when such is requested:
func retrieveUserText() -> String
{
// Since there may be no text entered (for which reason the outlet text is
// defined as optional String), we should have a fall-back value (here,
// empty string).
return userText.text ?? ""
}
Simple enough, no?
Setting up the parent/target view
Now let’s hook up a data recipient (in this instance, a text view) in the parent storyboard to an outlet in its view controller, say displayUserText.
We provide an unwind function, but as we wish to do more than just provide a hierarchical return point, we need to specify the action to be taken when it is invoked. Let’s say the child’s view controller was called EnterTextViewController; we might do something like this:
// Generic unwind function which can be targeted by various children.
@IBAction func unwindFromChildViewController( unwindSegue: UIStoryboardSegue )
{
// Attempt to retrieve the information from the child view.
// In this example, there is only one possible source, namely
// EnterTextViewController, so the test will always succeed. In a more
// complex scenario, we might have a switch statement.
if let SourceViewController = unwindSegue.source as? EnterTextViewController
{
// Get the data being passed back by means of the function
// set up for that purpose.
displayUserText.text = SourceViewController.retrieveUserText()
}
}
Now, in the simulator, this works fine, but on an actual mobile device, we can run into a problem, since we need a visual keyboard to enter the text, and chances are that it might obscure the control button which accepts the text and sparks the unwind operation ….
Dismissing the keyboard
In a situation like this, we will want to add some special functionality to cover the approaches we wish to use to get rid of the keyboard on demand.
Using a tap gesture
The first approach is to add a tap recognizer to the view in question which performs the required action. A search in the object library gives us Tap Gesture Recognizer which we drag to the view in the storyboard. And, of course, we must establish a connection to an action in the corresponding controller, with the following code:
@IBAction func tappedView(_ sender: Any)
{
// The action should be linked to the field responsible
// for bringing up the keyboard.
userText.resignFirstResponder()
}
‘First responder’ means the object currently in control of the field, in this instance, the keyboard. Causing it to resign therefore returns the keyboard to the hidden state.
Using the return key
So … what if they keyboard should be dismissed when, say, the user hits the enter key?
In this instance, it is necessary to create an action associated with the field which is associated with the event Did End On Exit which should execute the same code.
By the way, if/when such actions are desired for multiple fields, it is necessary to enter code to resign the first responder for each of them.