Do not index
Do not index
From left to right: NavigationController, OriginController, TopBottomController, LeftRightController
Today I would like to answer a question I got quiet often lately. How do you transfer information between UIViewControllers that are connected with a UINavigationController.
This scenario is very common. A typical scenario is the Settings App of the iPhone’s iOS. We navigate from controller A to controller B using a navigation controller, we send information from A to B and from B back to A.
Example: iOS Accessibility Settings
In this article I will talk about the 2 most common approaches. A segue using the storyboard and the init with dependency.
The Keyword: Delegate
The idea with our small test project is that we have an origin controller with two buttons and a label. If we press the “Pick Navigation” button, we will present the TopBottomController using a segue. And on “Pick Presentation” we will present the LeftRightController.
If we press one of the buttons in TopBottomController or LeftRightController, we will dismiss/pop the controllers and send the selection back to the origin controller and present the selection in the label.
TopBottomController will have the TopBottomControllerDelegate and LeftRightController the LeftRightControllerDelegate.
TopBottomControllerDelegate
protocol TopBottomControllerDelegate {
func topBottomController(topButtonPressed: Bool)
}
LeftRightControllerDelegate
protocol LeftRightControllerDelegate {
func leftRightController(leftButtonPressed: Bool)
}
Connecting the delegates
When presenting our TopBottomController we’re using a storyboard segue by calling:
@IBAction func navigationButtonPressed(_ sender: Any) {
performSegue(withIdentifier: "navigationSegue", sender: self)
}
To connect the segue, we need to prepare the destination controller in the method: prepareForSegue:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "navigationSegue" {
let controller = segue.destination as! TopBottomController
controller.delegate = self
}
}
Next we need to implement the TopBottomControllerDelegate into our OriginController. And handle the action (in our case update the label).
// MARK: - TopBottomControllerDelegate
extension OriginController: TopBottomControllerDelegate {
func topBottomController(topButtonPressed: Bool) {
self.selectionLabel.text = topButtonPressed ? "Top" : "Bottom"
}
}
Same for our LeftRightControllerDelegate.
// MARK: - LeftRightControllerDelegate
extension OriginController: LeftRightControllerDelegate {
func leftRightController(leftButtonPressed: Bool)
self.selectionLabel.text = leftButtonPressed ? "Left" : "Right"
}
}
And now to be able to send the selected button back from our TopBottomController to our OriginController, all we need to implement is the following into our TopBottomController:
class TopBottomController: UIViewController { public var delegate: TopBottomControllerDelegate! @IBAction func topButtonPressed(_ sender: Any) {
self.navigationController?.popViewController(animated: true)
self.delegate.topBottomController(topButtonPressed: true)
} @IBAction func bottomButtonPressed(_ sender: Any) {
self.navigationController?.popViewController(animated: true)
self.delegate.topBottomController(topButtonPressed: false)
}
}
As you can see, we pop the controller using our navigationController and then call the delegate with our selection. The result will be the selected button presented in our origin controller’s label.
The same logic applies to our dependency injected LeftRightController. When we press the “Pick Presentation” button, we’re presenting the controller that we’re initiating as following:
private var leftRightController: LeftRightController {
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "LeftRightController") as! LeftRightController
controller.delegate = self
return controller
}
As you can see, we are here as well defining the delegate to self. Lastly we just need to call our controller:
@IBAction func presentationButtonPressed(_ sender: Any) {
self.present(leftRightController, animated: true)
}
And dismiss and call our delegate when the user clicks on of the buttons.
class LeftRightController: UIViewController { public var delegate: LeftRightControllerDelegate! @IBAction func leftButtonPressed(_ sender: Any) {
self.dismiss(animated: true)
self.delegate.leftRightController(leftButtonPressed: true)
} @IBAction func rightButtonPressed(_ sender: Any) {
self.dismiss(animated: true)
self.delegate.leftRightController(leftButtonPressed: false)
}
}
And as a result our label will now present the left/right selection as expected.
I hope this story explained to you the basics and power of the delegates. Of course we could have cleaned the code, refactored and made it very fancy, but I wanted to keep it simple so everyone can understand it. Leave some love 🙂