Я создал пользовательский контроллер контейнера, который работает аналогично UIPageViewController
, чтобы я мог реализовать некоторые пользовательские переходы и логику источника данных. Я попытался подражать тому, как новые API-интерфейсы API-интерфейса для контроля доступа работают в iOS 7, и он хорошо работает, за исключением некоторых раздражающих причуд с обратными вызовами внешнего вида, когда переход отменен...
А именно, при выполнении перехода, когда нужно называть beginAppearanceTransition:animated:
и endAppearanceTransition
?
В моем пользовательском классе контейнера есть такой код:
- (BOOL)shouldAutomaticallyForwardAppearanceMethods
{
return NO; // Since the automatic callbacks are wrong for our custom transition.
}
- (void)startTransition:(CustomTransitionContext *)context
{
// Get reference to the view controllers involved in the transition.
UIViewController *oldVC = [context viewControllerForKey:UITransitionContextFromViewController];
UIViewController *newVC = [context UITransitionContextToViewController];
// Prepare parent/child relationship changes.
[oldVC willMoveToParentViewController:nil];
[self addChildViewController:newVC];
// Begin view appearance transitions.
[oldVC beginAppearanceTransition:NO animated:[context isAnimated]];
[newVC beginAppearanceTransition:YES animated:[context isAnimated]];
// Register a completion handler to run when the context completeTransition: method is called.
__weak CustomContainerController *weakSelf = self;
context.transitionCompletionHandler = ^(BOOL complete) {
// End appearance transitions here?
[oldVC endAppearanceTransition];
[newVC endAppearanceTransition];
if (complete) {
// Or only if the transition isn't cancelled; here?
[oldVC endAppearanceTransition];
[newVC endAppearanceTransition];
[oldVC removeFromParentViewController];
[newVC didMoveToParentViewController:weakSelf];
} else {
[newVC removeFromParentViewController];
[oldVC didMoveToParentViewController];
}
}
if ([context isInteractive] && [self.transitionController conformsToProtocol:@protocol(UIViewControllerInteractiveTransitioning)]) {
// Start the interactive transition.
[self.transitionController startInteractiveTransition:context];
} else if ([context isAnimated] && [self.transitionController conformsToProtocol:@protocol(UIViewControllerAnimatedTransitioning)]) {
// Start the animated transition.
[self.transitionController animateTransition:context];
} else {
// Just complete the transition.
[context completeTransition:YES];
}
}
Итак, если я вызываю endAppearanceTransition
независимо от того, был ли переход отменен, тогда мои обратные вызовы вида выглядят так, когда переход отменяется:
oldVC viewWillDisappear: // Fine
newVC viewWillAppear: // Fine
// ... some time later transition is cancelled ...
oldVC viewDidDisappear: // Wrong! This view controller view is staying.
newVC viewDidAppear: // Wrong! The appearance of this view controllers view was cancelled.
Если я вызываю endAppearanceTransition
только когда переход завершен успешно, сначала все выглядит лучше:
oldVC viewWillDisappear: // Fine
newVC viewWillAppear: // Fine
// ... some time later transition is cancelled ...
// ... silence. (which is correct - neither view actually appeared or disappeared,
// and I can undo side effects in viewWill(Dis)Appear using the
// transitionCoordinator object)
Но затем, в следующий раз, когда я начну переход, я получаю отсутствие обратных вызовов внешнего вида. Следующий набор обратных вызовов вида появляется только после beginAppearanceTransition:animated:
, за которым следует вызов endApperanceTransition
. Стоит отметить, что я не получаю сообщение "Неуравновешенные вызовы для начала/окончания видимых переходов для консоли управления ViewController".
В WWDC 2013 Session 218 (пользовательские переходы с использованием контроллеров просмотров) Брюс Нило делает довольно уместную шутку о своих коллегах, говорящих ему, что viewWillAppear:
и viewWillDisappear:
действительно следует называть viewMightAppear:
и viewMightDisappear:
(см. раздел этого сеанса, начинающийся в 42:00). Учитывая, что мы сейчас находимся в сфере отмены интерактивных жестов, нам кажется, что нам нужен метод cancelAppearanceTransition
(или endAppearanceTransition:(BOOL)finished
) для пользовательских контейнеров.