¿Cómo puedo saber cuándo GKHostedAuthenticateViewController ha desaparecido?

Estoy tratando de detectar cuando la vista de authentication de usuario GameKit de Apple se descarta y se desactiva en una aplicación Cocos2D. Quiero mostrar otra vista justo después de que el usuario esté autenticado, así que estoy tratando de encontrar un desencadenador adecuado.

Esto es lo que estoy haciendo: siguiendo la guía de progtwigción de Game Center de Apple, estoy buscando [[GKLocalPlayer localPlayer] isAuthenticated] y si el usuario no está autenticado, estableceré authenticateHandler y saveé el viewController devuelto así:

 [GKLocalPlayer localPlayer].authenticateHandler = ^(UIViewController *viewController, NSError *error){ if (viewController != nil) { //save the returned view controller to show it when user tries to access leaderboards, etc. _savedGCAuthViewController = viewController; } else if([[GKLocalPlayer localPlayer] isAuthenticated]){ _userAuthenticated = YES; } }; 

Guardo el viewController devuelto para que pueda mostrarlo en un momento más apropiado como este:

 AppController *appDelegate = (AppController *)[[UIApplication shanetworkingApplication] delegate]; [appDelegate.navController presentViewController:_savedGCAuthViewController animated:YES completion:nil]; 

Todo funciona bien. El problema es que no tengo forma de saber cuándo desaparece esa vista.

Todas las otras preguntas / respuestas que veo aquí en StackOverflow dicen subclass el viewController en una situación como esta para acceder al método viewDidDisappear, pero no puedo subclass el controller de vista porque Apple no proporciona acceso a los encabezados para el Clase GKHostedAuthenticateViewController.

También he buscado todos los posts de notificación para acceder, pero no parece que UIViewController utiliza ningún post de estilo NSNotificationCenter.

¿Ideas?

Soy relativamente nuevo en iOS, pero ¿hay alguna manera de acceder a la jerarquía de vista de alguna manera e insert mi propia vista que se elimina cuando GKHostedAuthenticateViewController lo hace? Tal vez como un controller de vista padre o hijo?

¡Gracias!

Puede envolver GKHostedAuthenticateViewController en un controller propio antes de presentarlo, para que pueda acceder a los methods viewDidDisappear / viewWillDisappear:

 // Simple class to wrap GKHostedAuthenticateViewController class AuthController: UINavigationController { var viewWillDisappearHandler: ((Bool) -> ())? convenience init(authController: UIViewController) { self.init(rootViewController: authController) self.modalInPopover = true self.modalPresentationStyle = UIModalPresentationStyle.FormSheet self.navigationBarHidden = true self.prefernetworkingContentSize = authController.prefernetworkingContentSize } override func viewWillDisappear(animated: Bool) { super.viewWillDisappear(animated) viewWillDisappearHandler?(animated) } } 

Luego, en su GKLocalPlayer.authenticateHandler , puede configurar su viewWillDisappearHandler:

 GKLocalPlayer.localPlayer().authenticateHandler = { (viewController: UIViewController?, error: NSError?) -> Void in if let viewController = viewController { let authController = AuthController(authController: viewController) authController.viewWillDisappearHandler = { (animated: Bool) -> () in println("viewController is disappearing") } self.presentViewController(authController, animated: true, completion: nil) } else { // Authenticated } } 

Por alguna razón, el controller de vista de authentication de Game Center es una instancia de GKHostedAuthenticateViewController que es una class privada a la que no podemos usar ni hacer reference. No nos da ninguna forma de detectar de manera limpia cuándo se descarta (a diferencia de las instancias de GKGameCenterViewController que nos permiten a través del protocolo GKGameCenterControllerDelegate .

Esta solución (solución de lectura) funciona probando en segundo plano cada cuarto de segundo para cuando el controller de vista ha sido descartado. No es bonito, pero funciona.

El código a continuación debe ser parte de su presentación GKGameCenterControllerDelegate , que debe cumplir con el protocolo GKGameCenterControllerDelegate .

Swift y Objective-C proporcionado.

 // Swift func authenticateLocalUser() { if GKLocalPlayer.localPlayer().authenticateHandler == nil { GKLocalPlayer.localPlayer().authenticateHandler = { (gameCenterViewController: UIViewController?, gameCenterError: NSError?) in if let gameCenterError = gameCenterError { log.error("Game Center Error: \(gameCenterError.localizedDescription)") } if let gameCenterViewControllerToPresent = gameCenterViewController { self.presentGameCenterController(gameCenterViewControllerToPresent) } else if GKLocalPlayer.localPlayer().authenticated { // Enable GameKit features log.debug("Player already authenticated") } else { // Disable GameKit features log.debug("Player not authenticated") } } } else { log.debug("Authentication Handler already set") } } func testForGameCenterDismissal() { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(0.25 * Double(NSEC_PER_SEC))), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) { if let presentedViewController = self.presentedViewController { log.debug("Still presenting game center login") self.testForGameCenterDismissal() } else { log.debug("Done presenting, clean up") self.gameCenterViewControllerCleanUp() } } } func presentGameCenterController(viewController: UIViewController) { var testForGameCenterDismissalInBackground = true if let gameCenterViewController = viewController as? GKGameCenterViewController { gameCenterViewController.gameCenterDelegate = self testForGameCenterDismissalInBackground = false } presentViewController(viewController, animated: true) { () -> Void in if testForGameCenterDismissalInBackground { self.testForGameCenterDismissal() } } } func gameCenterViewControllerDidFinish(gameCenterViewController: GKGameCenterViewController!) { gameCenterViewControllerCleanUp() } func gameCenterViewControllerCleanUp() { // Do whatever needs to be done here, resume game etc } 

Nota: las llamadas log.error y log.debug están haciendo reference a XCGLogger: https://github.com/DaveWoodCom/XCGLogger

 // Objective-C - (void)authenticateLocalUser { GKLocalPlayer* localPlayer = [GKLocalPlayer localPlayer]; __weak __typeof__(self) weakSelf = self; if (!localPlayer.authenticateHandler) { [localPlayer setAuthenticateHandler:(^(UIViewController* viewcontroller, NSError* error) { if (error) { DLog(@"Game Center Error: %@", [error localizedDescription]); } if (viewcontroller) { [weakSelf presentGameCenterController:viewcontroller]; } else if ([[GKLocalPlayer localPlayer] isAuthenticated]) { // Enable GameKit features DLog(@"Player already authenticated"); } else { // Disable GameKit features DLog(@"Player not authenticated"); } })]; } else { DLog(@"Authentication Handler already set"); } } - (void)testForGameCenterDismissal { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ if (self.presentedViewController) { DLog(@"Still presenting game center login"); [self testForGameCenterDismissal]; } else { DLog(@"Done presenting, clean up"); [self gameCenterViewControllerCleanUp]; } }); } - (void)presentGameCenterController:(UIViewController*)viewController { BOOL testForGameCenterDismissalInBackground = YES; if ([viewController isKindOfClass:[GKGameCenterViewController class]]) { [(GKGameCenterViewController*)viewController setGameCenterDelegate:self]; testForGameCenterDismissalInBackground = NO; } [self presentViewController:viewController animated:YES completion:^{ if (testForGameCenterDismissalInBackground) { [self testForGameCenterDismissal]; } }]; } - (void)gameCenterViewControllerDidFinish:(GKGameCenterViewController*)gameCenterViewController { [self gameCenterViewControllerCleanUp]; } - (void)gameCenterViewControllerCleanUp { // Do whatever needs to be done here, resume game etc } 

Publique su propia NSNotification de authenticateHandler para indicar que ha finalizado. Su controller de vista de presentación debe escuchar esta notificación.