1

I have a working simple single player game, where the initial view controller has a button to start the game. This button performs a segue and all game logic in the GameViewController is working as expected.

I've followed this tutorial to add multi player functionality to my game. On the initial view controller, a button now calls

GameKitHelper.sharedGameKitHelper.findMatchWithMinPlayers(2, maxPlayers: 2, viewController: self, delegate: MultiPlayerNetworking)
           }

which has the following implementation in GameKitHelper.swift:

func findMatchWithMinPlayers (minPlayers: Int, maxPlayers: Int, viewController: UIViewController, delegate: GameKitHelperDelegate) {

        matchStarted = false
        let request = GKMatchRequest()
        self.delegate = delegate

        request.minPlayers = 2
        request.maxPlayers = 2

        presentingViewController = viewController
        presentingViewController.dismissViewControllerAnimated(false, completion: nil)
        let mmvc = GKMatchmakerViewController(matchRequest: request)
        mmvc?.matchmakerDelegate = self
        presentingViewController.presentViewController(mmvc!, animated: true, completion: nil)

        self.delegate?.matchStarted()
        }

The Class MultiPlayerNetworking implements the GameKitHelper protocol, and gets called on the matchStarted function. The MultiPlayerNetworking class in essence takes over here, and starts sending out messages to hosts and remote players.

Note that some time later, When auto-matching finishes, the following function gets called in GameKitHelper:

func matchmakerViewController(viewController: GKMatchmakerViewController, didFindMatch match: GKMatch) {


    viewcontroller.dismissViewControllerAnimated(true, completion: {})

    self.match = match
    match.delegate = self

}

Now, I think this says that the GKMatchmakerViewController is dismissed, thereby showing me the initial view controller again (and this is what happens on screen).

Now my issue! After the GKMatchmakerViewController is dismissed, I'm back at the initial view controller and want to 'simulate' an automatic segue to my gameView (which has logic to deal with a multi player game as well).

I've made the initial view controller conform to the MultiPlayerNetworking protocol, which has a function to simulate a segue:

func segueToGVC() {
    self.performSegueWithIdentifier("game", sender: nil)  // self = initial view controller  
}

However, xCode complains with:

Warning: Attempt to present <GameViewController: 0x7d440050> on <GKMatchmakerViewController: 0x7c8fbc00> whose view is not in the window hierarchy!

I'm stuck here, and have tried so many different methods of dismissing the view controller, to making sure I'm calling the performSegue function on the topViewController via this link, but nothing works.

My question: why is the GKMatchmakerViewController visually dismissed, but still present in the view hierarchy, such that calling a performSegue function on the initial view controller give the above error/warning?

Views are greatly appreciated!

1 Answers1

1

why is the GKMatchmakerViewController visually dismissed, but still present in the view hierarchy

Here are two suggestions:

  • Perhaps it's because dismissal takes time. You are saying:

    viewcontroller.dismissViewControllerAnimated(true, completion: {})
    

    So there's an animation. Don't attempt to perform the next segue until the animation is over.

  • Perhaps you are just wrong about who self is. You are saying:

    self.performSegueWithIdentifier("game", sender: nil)  
    // self = initial view controller  
    

    We have only your word, in that comment, for who self is. Meanwhile, the runtime seems to think differently about the matter:

    Attempt to present <GameViewController: 0x7d440050> on <GKMatchmakerViewController: 0x7c8fbc00>

    It might be good to believe the runtime; after all, it knows more than you do.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • Thans matt. On the self question: if I'm in my class initial view controller and I'm in a function within the view controller, can self really be anything different? If the answer is yes, I really need to read up on this stuff and / or change my current thinking. – Sander de Ruiter Mar 20 '16 at 18:15
  • On dismissing taking time, how can I make sure the discussion is done before I attempt to perform another segue? – Sander de Ruiter Mar 20 '16 at 18:25
  • Well, I don't know where you are calling `segueToGVC`. But to follow a dismissal with a new presentation, you'd want to do the presentation starting in the `completion` of the dismissal. That is what completion _means_. – matt Mar 20 '16 at 18:30
  • SegueToGVC is a function in the initial view controller that conforms to the multiPlayerNetworking protocol. I'm assuming that self there refers to the class the function is embedded in and NOT to the class it is the delegate of. I'll try to give the completion block a try. Thanks. – Sander de Ruiter Mar 20 '16 at 18:35
  • By the way there is a really easy way to test the "dismissal takes time" theory: say `false` instead of `true`. Now there is no animation. If everything works now (except for the lack of animation), the animation was the problem. :) – matt Mar 20 '16 at 18:55
  • That was on my mind as well. Unfortunately, I can't use the completion block. When a game is started, I send messages in the multiPlayerNetworking class to setup the players and from there I need to go to the gameView. – Sander de Ruiter Mar 20 '16 at 18:57
  • You have not explained your architecture at all. I don't know what any of these classes are or how they relate. If you want precise help, give actual info. In the absence of details, I've tried to give you some guidelines for working on the problem yourself. – matt Mar 20 '16 at 19:04
  • Point taken. I originally had a question ready to ask which detailed most of the structure, with lots of code snippets for the classes and functions, but that quickly became way too much to read, hence my abbreviated, code-light question. If this doesn't work, I'll try post my longer question. – Sander de Ruiter Mar 20 '16 at 19:11
  • You were right, marked your answer as answering the question. I changed animation to false and the error message disappeared. Thanks again. – Sander de Ruiter Mar 20 '16 at 19:51
  • Cool. And now you have to work out some sort of architecture so that the `completion` handler can signal to the other view controller that it's time to do the next segue, I presume. – matt Mar 20 '16 at 20:17
  • I was having this same issue, but setting animations to 'false' wasn't resolving it. I wasn't immediately presenting another vc, so had just assumed enough time was left to finish the dismiss. Adding `DispatchQueue.main.asyncAfter(deadline: .now() + 0.3)` was enough extra time to allow the GKMatchmakerViewController to do it's stuff and go away. Obviously this is a hideous solution though... so I'm now using the completion block as intended to call the remaining code. Thanks for the pointer in the right direction! – jt_uk Apr 17 '20 at 23:27