También comenzamos a tener este problema, y era muy probable que los nuestros fueran causados por el mismo problema.
En nuestro caso, tuvimos que extraer datos del back-end en algunos casos, lo que significaba que un usuario podía tocar algo y luego habría un ligero retraso antes de que ocurriera el empuje de navegación. Si un usuario estaba haciendo tapping rápidamente, podría terminar con dos pulsaciones de navegación desde el mismo controlador de vista, lo que provocó esta misma excepción.
Nuestra solución es una categoría en UINavigationController que evita los empujes / estallidos a menos que el vc superior sea el mismo desde un punto dado en el tiempo.
archivo .h:
@interface UINavigationController (SafePushing)
- (id)navigationLock; ///< Obtain "lock" for pushing onto the navigation controller
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated navigationLock:(id)navigationLock; ///< Uses a horizontal slide transition. Has no effect if the view controller is already in the stack. Has no effect if navigationLock is not the current lock.
- (NSArray *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated navigationLock:(id)navigationLock; ///< Pops view controllers until the one specified is on top. Returns the popped controllers. Has no effect if navigationLock is not the current lock.
- (NSArray *)popToRootViewControllerAnimated:(BOOL)animated navigationLock:(id)navigationLock; ///< Pops until there's only a single view controller left on the stack. Returns the popped controllers. Has no effect if navigationLock is not the current lock.
@end
archivo .m:
@implementation UINavigationController (SafePushing)
- (id)navigationLock
{
return self.topViewController;
}
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated navigationLock:(id)navigationLock
{
if (!navigationLock || self.topViewController == navigationLock)
[self pushViewController:viewController animated:animated];
}
- (NSArray *)popToRootViewControllerAnimated:(BOOL)animated navigationLock:(id)navigationLock
{
if (!navigationLock || self.topViewController == navigationLock)
return [self popToRootViewControllerAnimated:animated];
return @[];
}
- (NSArray *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated navigationLock:(id)navigationLock
{
if (!navigationLock || self.topViewController == navigationLock)
return [self popToViewController:viewController animated:animated];
return @[];
}
@end
Hasta ahora, esto parece haber resuelto el problema para nosotros. Ejemplo:
id lock = _dataViewController.navigationController.navigationLock;
[[MyApi sharedClient] getUserProfile:_user.id success:^(MyUser *user) {
ProfileViewController *pvc = [[ProfileViewController alloc] initWithUser:user];
[_dataViewController.navigationController pushViewController:pvc animated:YES navigationLock:lock];
}];
Básicamente, la regla es: antes de cualquier retraso no relacionado con el usuario tome un bloqueo del controlador de navegación relevante e inclúyalo en la llamada para pulsar / abrir.
La palabra "bloqueo" puede ser una redacción ligeramente pobre, ya que puede insinuar que hay alguna forma de bloqueo que necesita desbloqueo, pero como no hay un método de "desbloqueo" en ningún lado, probablemente esté bien.
(Como nota al margen, "demoras no relacionadas con el usuario" son cualquier demora que el código está causando, es decir, cualquier cosa asincrónica. Los usuarios que tocan un controlador de navegación que se empuja animadamente no cuentan y no hay necesidad de hacer la versión navigationLock: para esos casos.)