1
votes

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)
      }
    }
  }

 }
1

1 Answers

1
votes

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