I'm getting in a muddle trying to pass data between two viewcontrollers that are displayed on one screen via container views.
Minimal example below - top view (TopVC) has a textInput field. When I press the button, I want the label on the bottom view (BottomVC) to display the inputted text. Moreover, I want it to pass back a message to the TopVC and update the topVC label with the message "Successfully contacted bottom VC"
Storyboard set up
I have no idea now to reference the view controllers from each other basically.
class TopViewController: UIViewController {
@IBOutlet weak var textInput: UITextField!
@IBOutlet weak var textOutput: UILabel!
@IBAction func go(_ sender: UIButton) {
// ???????????
}
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.blue
}
func callMeBaby(greeting: String) {
textOutput.text = greeting
}
}
In the ????? placeholder, I want to put something that basically works like BottomVC.test(textInput.text, callmebaby) - but obviously I need to put in some extra code to 'introduce' the two ViewControllers, and I'm not sure what to do.
class BottomViewController: UIViewController {
@IBOutlet weak var textLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.yellow
}
func test(input: String, completion: (String) -> Void) {
textLabel.text = input
completion("Successfully contacted bottom VC")
}
}
Start with creating delegates for both of your container ViewControllers. Don't forget to add : class
. If you didn't do it, you wouldn't be able to create weak delegate variable:
protocol TopViewControllerDelegate: class {
func sendMessage(_ string: String)
}
protocol BottomViewControllerDelegate: class {
func sendMessage(_ string: String)
}
Now for every container ViewController create weak delegate variable
class TopViewController: UIViewController {
weak var delegate: TopViewControllerDelegate?
...
}
class BottomViewController: UIViewController {
weak var delegate: BottomViewControllerDelegate?
...
}
then for TopVC implement Bottom's delegate and for BottomVC Top's.
extension TopViewController: BottomViewControllerDelegate {
func sendMessage(_ string: String) {
// do something
}
}
extension BottomViewController: TopViewControllerDelegate {
func sendMessage(_ string: String) {
// do something
}
}
Your segues between main ViewController and containers should have their own identifiers: EmbedTop
, EmbedBottom
.
So in your WrapperViewController
create variable for your Top and Bottom ViewController and override method prepare(for:sender:)
and inside assign these variables
class WrapperViewController: UIViewController {
var topVC: TopViewController?
var bottomVC: BottomViewController?
...
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "EmbedTop" {
topVC = segue.destination as! TopViewController
} else if segue.identifier == "EmbedBottom" {
bottomVC = segue.destination as! BottomViewController
}
}
}
finally in viewDidAppear
set delegate of TopVC's as BottomVC and of BottomVC's as TopVC
override func viewDidAppear(_ animated: Bool) {
topVC.delegate = bottomVC
bottomVC.delegate = topVC
}
Now your two ViewControllers can speak with each other! :-)
Example:
class BottomViewController: UIViewController {
...
func speakToTop() {
delegate?.sendMessage("Hi Top!")
}
}