Summary of problem
I have two View Controllers (VC1 = MainViewController and VC2 = ResultViewController). In VC1 there is a method called endGame(). In this method I want to both dismiss VC1 and present VC2.
In VC2 there is a button function called playAgainButton(). In this function I want to both dismiss VC2 and present VC1.
when I try to first dismiss VC1 and then present VC2, VC1 cannot present VC2 because VC1 is already dismissed and doesn't exist in the stack.
dismiss(animated: true) {
self.present(rvc, animated: true, completion: nil)
}
When I try to first present VC2 and then dismiss VC1, then VC2 appears for a 1 second and then immediately disappears.
present(rvc, animated: true) {
self.dismiss(animated: true, completion: nil)
}
What I have tried
I have come up with similar problems:
I use the latest swift and Xcode version.
My code
VC1
// protocol for presenting View Controllers
protocol VcDelegate: AnyObject {
func presentVc(vc: UIViewController)
}
// The main view controller of the game.
class MainViewController: UIViewController, UICollectionViewDataSource,
UICollectionViewDelegateFlowLayout, UICollectionViewDelegate,
UIGestureRecognizerDelegate, VcDelegate {
// MARK: - Protocol functions & properties
func presentVc(vc: UIViewController) {
present(vc, animated: false, completion: nil)
}
// instance of VcDelegate protocol
weak var mvcDelegate: VcDelegate?
// MARK: - Properties
// the level of the game
var level: Int = 0
// the player's score
var score: Int = 0
/// Terminate the game for the next level and show results page
private func gameOver() {
// present the resultViewController
if let rvc = storyboard?.instantiateViewController(withIdentifier: "ResultViewController") as? ResultViewController {
rvc.resultLevel = level
rvc.resultScore = score
rvc.rvcDelegate = self
// dismiss MainViewController
dismiss(animated: true) {
// present ResultViewController by using instance of VcDelegate protocol
self.mvcDelegate?.presentVc(vc: rvc)
}
}
}
}
VC2
// This page is viewed after a level is finished. It shows level results and play again button.
class ResultViewController: UIViewController, VcDelegate {
// MARK: - Protocol functions & properties
func presentVc(vc: UIViewController) {
present(vc, animated: false, completion: nil)
}
weak var rvcDelegate: VcDelegate?
// MARK: - Properties
// variable showing game level
var resultLevel = 0
// variable showing current score
var resultScore = 0
/// When play again button is tapped a new game starts (a new mainViewController is presented)
@IBAction func playAgainButton(_ sender: UIButton) {
// present a new MainViewController
if let mvc = storyboard?.instantiateViewController(withIdentifier: "MainViewController") as? MainViewController {
mvc.level = resultLevel
mvc.score = resultScore
mvc.mvcDelegate = self
// dismiss ResultViewController
dismiss(animated: true) {
// present MainViewController by using instance of VcDelegate protocol
self.rvcDelegate?.presentVc(vc: mvc)
}
}
}
}
The problem is that when you create the VC1
and you pass self
as mvcDelegate
, you actually passing VC2
which is about to be dismissed and after the dismiss VC2
cannot present any view controller.
You probably need to pass the delegate of the one view controller to the other before you present it:
// Pass your delegate to the other view controller delegate
mvc.mvcDelegate = rvcDelegate
// dismiss ResultViewController
dismiss(animated: true) {
// present MainViewController by using instance of VcDelegate protocol
self.rvcDelegate?.presentVc(vc: mvc)
}
And the other way around:
// Pass your delegate to the other view controller delegate
rvc.rvcDelegate = mvcDelegate
// dismiss MainViewController
dismiss(animated: true) {
// present ResultViewController by using instance of VcDelegate protocol
self.mvcDelegate?.presentVc(vc: rvc)
}
As mentioned in the comments VC1 is the rootViewController
of the application window. So, to achieve what you are looking for, you need to replace the rootViewController
.
In MainViewController:
// Change rootViewController of the view's window with ResultViewController
view.window?.rootViewController = rvc
And the same in ResultViewController:
// Change rootViewController of the view's window with MainViewController
view.window?.rootViewController = mvc