Reputation: 7885
I want to call a method on the presenting ViewController when I dismiss a popup ViewController. I start the Popup from the 'MainViewController' like this: (With help of EzPopup Library, but I think that shouldn't matter here)
@IBAction func onStartWorkout(_ sender: UIButton) {
let startWorkoutVC = storyboard!.instantiateViewController(withIdentifier: "CardContent") as! StartWorkout_ViewController
let popupVC = PopupViewController(contentController: startWorkoutVC, popupWidth: 300, popupHeight: 500)
popupVC.cornerRadius = 10
present(popupVC, animated: true)
}
then when I dismiss the Popup I do this:
func dissmissPopup() {
if let presenter = presentingViewController as? MainViewController {
presenter.startWorkout(index: 0, isSNR: true)
}
self.dismiss(animated: true, completion: nil)
}
But the method won't be called. How does presentingViewController work and why doesn't my reference to MainViewController work?
Upvotes: 1
Views: 3361
Reputation: 318804
Assuming dissmissPopup
is in your StartWorkout_ViewController
class then presentingViewController
will be nil
because you are not presenting StartWorkout_ViewController
, you are presenting PopupViewController
. The presentingViewController
of PopupViewController
should be your MainViewController
.
Depending on how PopupViewController
is written, you should be able to get what you need by accessing parent
(giving you the PopupViewController
, and then accessing its presentingViewController
.
func dissmissPopup() {
if let presenter = parent?.presentingViewController as? MainViewController {
presenter.startWorkout(index: 0, isSNR: true)
}
self.dismiss(animated: true, completion: nil)
}
Now, having said all of that, don't do this. It's a poor, fragile design. You are hardcoding into StartWorkout_ViewController
the knowledge that MainViewController
needs some specific method called.
The proper solution is to define a protocol and a delegate. Then StartWorkout_ViewController
simply calls the protocol method on its delegate (not caring who it really is). And MainViewController
sets itself as StartWorkout_ViewController
's delegate and implements the protocol's methods.
This approach eliminates the need for StartWorkout_ViewController
to know who presented it or how. It eliminates the need for StartWorkout_ViewController
to know to specifically call a method named startWorkout
. It also allows other classes to present MainViewController
and do whatever it needs when it is dismissed without further hardcoded changes to MainViewController
.
Here's a rough outline of implementing this with a protocol and delegate:
StartWorkout_ViewController.swift:
protocol StartWorkoutDelegate: class {
func complete() // add any necessary parameters
}
class StartWorkout_ViewController: UIViewController {
weak var delegate: StartWorkoutDelegate?
func dissmissPopup() {
delegate?.complete() // add any necessary parameters
self.dismiss(animated: true, completion: nil)
}
}
MainViewController.swift:
class MainViewController: UIViewController, StartWorkoutDelegate {
@IBAction func onStartWorkout(_ sender: UIButton) {
let startWorkoutVC = storyboard!.instantiateViewController(withIdentifier: "CardContent") as! StartWorkout_ViewController
startWorkoutVC.delegate = self
let popupVC = PopupViewController(contentController: startWorkoutVC, popupWidth: 300, popupHeight: 500)
popupVC.cornerRadius = 10
present(popupVC, animated: true)
}
func complete() {
startWorkout() // add any necessary parameters
}
}
Upvotes: 3