Мы также начали сталкиваться с этой проблемой, и, скорее всего, наша проблема была вызвана той же проблемой.
В нашем случае нам приходилось извлекать данные из серверной части в некоторых случаях, что означало, что пользователь может что-то нажать, и тогда перед навигацией будет небольшая задержка. Если пользователь быстро переключается, он может получить два навигационных нажатия от одного и того же контроллера представления, что вызвало именно это исключение.
Наше решение - это категория в 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];
}];
По сути, правило таково: перед любыми задержками, не связанными с пользователем, захватите блокировку у соответствующего контроллера Nav и включите ее в вызов push / pop.
Слово «блокировка» может быть слегка некачественным, поскольку оно может указывать на наличие какой-либо формы блокировки, требующей разблокировки, но, поскольку нигде нет метода «разблокировки», вероятно, все в порядке.
(Как примечание, «не связанные с пользователем задержки» - это любые задержки, вызываемые кодом, то есть что-либо асинхронное. Пользователи, нажимающие на контроллер Nav, который анимированно толкается, не учитываются, и для них не нужно делать версию navigationLock: version). случаев.)