Zaczęliśmy również pojawiać się ten problem i było bardzo prawdopodobne, że nasz był spowodowany tym samym problemem.
W naszym przypadku musieliśmy w niektórych przypadkach pobierać dane z zaplecza, co oznaczało, że użytkownik mógł coś dotknąć, a następnie nastąpiło niewielkie opóźnienie przed wystąpieniem push nawigacji. Jeśli użytkownik szybko stukał, może skończyć z dwoma wypchnięciami nawigacji z tego samego kontrolera widoku, co wywołało ten wyjątek.
Nasze rozwiązanie to kategoria w UINavigationController, która zapobiega pchnięciom / wyskakiwaniu, chyba że górny vc jest taki sam z danego punktu w czasie.
plik .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
plik .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
Jak dotąd wydaje się, że rozwiązało to problem za nas. Przykład:
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];
}];
Zasadniczo obowiązuje zasada: przed jakimikolwiek opóźnieniami niezwiązanymi z użytkownikiem należy pobrać blokadę z odpowiedniego kontrolera nawigacyjnego i uwzględnić ją w wywołaniu funkcji push / pop.
Słowo „blokada” może być nieco słabo sformułowane, ponieważ może sugerować, że występuje jakaś forma blokady, która wymaga odblokowania, ale ponieważ nigdzie nie ma metody „odblokowania”, prawdopodobnie jest w porządku.
(Na marginesie, „opóźnienia niezwiązane z użytkownikiem” to wszelkie opóźnienia powodowane przez kod, tj. Wszystko asynchroniczne. Użytkownicy dotykający kontrolera nawigacyjnego, który jest animowany, nie liczą się i nie ma potrzeby wykonywania funkcji navigationLock: wersja dla tych przypadków.)