Ми також почали отримувати це питання, і, швидше за все, ймовірно, що наші були викликані тією ж проблемою.
У нашому випадку нам доводилося витягувати дані із заднього кінця в деяких випадках, що означало, що користувач може щось натиснути, і тоді буде невелика затримка до того, як відбудеться навігаційний поштовх. Якщо користувач швидко торкається, вони можуть закінчитися двома натисканнями навігації від одного контролера перегляду, що спричинило саме цей виняток.
Наше рішення - це категорія на UINavigationController, яка запобігає натисканням / спливанням, якщо верхній VC не є тим самим з заданого моменту часу.
.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
.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
Поки це, здається, вирішило проблему для нас. Приклад:
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];
}];
В основному це правило: перед будь-якими затримками, пов'язаними з користувачем, захопіть замок від відповідного контролера навігації та включіть його у виклик push / pop.
Слово «замок» може бути дещо поганим формулюванням, оскільки воно може містити певну форму блокування, яка потребує розблокування, але оскільки ніде немає способу «розблокувати», це, мабуть, добре.
(З іншого боку, "затримки, що не пов'язані з користувачем" - це будь-які затримки, які викликає код, тобто будь-яка асинхронність. Користувачі, що натискають на навигатор анімації, який анімовано натиснуто, не рахуються, і для цього немає необхідності робити НавігаціяLock: версія справ.)