Nous avons également commencé à avoir ce problème, et il est fort probable que le nôtre soit causé par le même problème.
Dans notre cas, nous devions extraire des données du back-end dans certains cas, ce qui signifiait qu'un utilisateur pouvait appuyer sur quelque chose, puis il y avait un léger délai avant que la poussée de navigation se produise. Si un utilisateur tapait rapidement, il pourrait se retrouver avec deux poussées de navigation du même contrôleur de vue, ce qui a déclenché cette exception.
Notre solution est une catégorie sur le UINavigationController qui empêche les poussées / pops à moins que le vc supérieur ne soit le même à partir d'un moment donné.
fichier .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
Fichier .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
Jusqu'à présent, cela semble avoir résolu le problème pour nous. Exemple:
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];
}];
Fondamentalement, la règle est la suivante: avant tout retard non lié à l'utilisateur, saisissez un verrou du contrôleur de navigation concerné et incluez-le dans l'appel à push / pop.
Le mot "verrouiller" peut être une formulation légèrement médiocre car il peut insinuer qu'il existe une forme de verrouillage qui doit être déverrouillée, mais comme il n'y a pas de méthode de "déverrouillage" nulle part, c'est probablement correct.
(En remarque, les "retards non liés à l'utilisateur" sont tous les retards que le code provoque, c'est-à-dire tout ce qui est asynchrone. Les utilisateurs qui tapent sur un contrôleur de navigation qui est poussé de manière animée ne comptent pas et il n'est pas nécessaire de faire le navigationLock: version pour ceux-ci cas.)