Пользовательская анимация перехода, не вызывающая методы жизненного цикла VC при увольнении
Итак, я построил настраиваемую презентационную переходную анимацию, и все, кажется, отлично работает, за исключением того, что методы жизненного цикла контроллера просмотра не вызываются при увольнении.
Перед представлением контроллера я использую стиль UIModalPresentationCustom
, чтобы поддерживать визуализацию VC в иерархии представлений, но как только я увольняю представленный VC, viewWillAppear и viewDidAppear не вызываются на моем контроллере представления. Я пропустил какой-то шаг, который мне нужно явно вызвать, чтобы запустить эти методы? Я знаю, что ручное вызов этих методов не является правильным решением.
Вот мой уклонный код анимации. Я в основном оживляю вид наложения формы, чтобы уменьшить размер ячейки представления коллекции при увольнении.
- (void)_animateDismissingTransitionWithContext:(id<UIViewControllerContextTransitioning>)transitionContext
{
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UICollectionView *destinationCollectionView = toCollectionViewController.collectionView;
UICollectionViewCell *destinationCollectionViewCell = [self _destinationCellFromContext:transitionContext];
UIView *containerView = transitionContext.containerView;
// Calculate frames
CGRect startFrame = fromEventDetailViewController.detailContainerView.frame;
CGRect endFrame = [destinationCollectionView convertRect:destinationCollectionViewCell.frame toView:containerView];
// Add overlay
UIView *overlayView = [UIView new];
overlayView.backgroundColor = [UIColor overlayBackground];
overlayView.frame = containerView.bounds;
overlayView.alpha = 1.0f;
[containerView addSubview:overlayView];
// Add fake detail container view
UIView *fakeContainerView = [UIView new];
fakeContainerView.backgroundColor = fromEventDetailViewController.detailContainerView.backgroundColor;
fakeContainerView.frame = startFrame;
[containerView addSubview:fakeContainerView];
// Hide from view controller
fromEventDetailViewController.view.alpha = 0.0f;
[UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0.0f usingSpringWithDamping:0.75f initialSpringVelocity:0.2f options:UIViewAnimationOptionCurveEaseOut animations:^{
fakeContainerView.frame = endFrame;
fakeContainerView.backgroundColor = [UIColor eventCellBackground];
overlayView.alpha = 0.0f;
} completion:^(BOOL finished) {
[fromEventDetailViewController.view removeFromSuperview];
[overlayView removeFromSuperview];
[fakeContainerView removeFromSuperview];
[transitionContext completeTransition:YES];
}];
}
Ответы
Ответ 1
Другим решением может быть использование beginAppearanceTransition: и endAppearanceTransition:. Согласно документации:
Если вы используете настраиваемый контроллер контейнера, используйте этот метод рассказать ребенку, что его взгляды скоро появятся или исчезнут. Делать не вызывает viewWillAppear:, viewWillDisappear:, viewDidAppear:, или viewDidDisappear: напрямую.
Вот как я их использовал:
- (void)animationEnded:(BOOL)transitionCompleted
{
if (!transitionCompleted)
{
_toViewController.view.transform = CGAffineTransformIdentity;
}
else
{
[_toViewController endAppearanceTransition];
}
}
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext
{
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
[toViewController beginAppearanceTransition:YES animated:YES];
// ... other code
}
Но я все еще считаю странным, что пользовательская модальная презентация не делает этого.
Ответ 2
После долгих споров с этой проблемой я нашел, что лучшее решение, которое работает в ios7 и ios8, заключается в том, чтобы оставить modalPresentationStyle = UIModalPresentationFullScreen
вместо UIModalPresentationCustom
, как предлагают документы.
Если я сделаю это, а также установлю transitioningDelegate
для моего делегата, он по-прежнему соблюдает мой переход, и методы will/diddisappear вызываются в контроллере вида "from". Кроме того: при загрузке нет никаких проблем с роутом-потом-поворот-потом-увольнением.
Ответ 3
Если вы используете UIModalPresentationCustom
, вы должны предоставить собственный UIPresentationController
класс, и если вы хотите использовать вызовы жизненного цикла ViewController, вам нужно переопределить shouldRemovePresentersView
и вернуть YES
.
Если вы хотите сохранить презентаторов и по-прежнему иметь обратный вызов жизненного цикла ViewControlelr, вы можете переопределить частный метод _shouldDisablePresentersAppearanceCallbacks
и вернуть NO
в свой пользовательский класс UIPresentationController
.
Ответ 4
А, это модальное представление. Я не верю, что viewWillAppear и viewDidAppear вызываются с настраиваемым переходом с использованием метода, поскольку представление технически все еще активно в иерархии представлений. Я бы предложил использовать делегацию здесь, как обычно, с представленным модальным.
Создать делегатский протокол на представленном VC. Создайте метод делегата, который можно вызвать из представляющего VC. Когда вы представляете наложение, установите отображающий VC в качестве делегата. Затем вызовите этот метод делегата из представленного VC. Затем вы можете вызывать любые действия из представленного VC, прежде чем вы вызовете dismissViewController
В вашем наложении (ModalViewController.h):
@protocol ModalViewDelegate <NSObject>
-(void)didDismissModalView;
@end
@interface ModalViewController : UIViewController
@property(strong, nonatomic) id <ModalViewDelegate> delegate;
В ModalViewController.m вызовите метод, который вызывает ваш метод делегирования:
- (void)dismissModal{
[self.delegate didDismissModalView];
}
В представленном файле VC h: (PresentingViewController.h) сделайте этот класс совместимым с вашим модальным протоколом делегирования:
@interface PresentingViewController : UIViewController <ModalViewDelegate>
В вашем представлении VC, когда вы представляете модальный:
...
ModalViewController *modalViewController = [[ModalViewController alloc] init];
modalViewController.delegate = self; //the makes the presenting VC the delegate
[self presentViewController:modalViewController animated:YES completion:nil];
...
Наконец, в вашем представлении VC, когда вы хотите выполнить некоторые действия до отклонения модальности, выполните метод ModalViewDelegate:
- (void)didDismissModalView{
//DO SOME COOL STUFF, SET UP STUFF HERE, UPDATE UI, ETC
//Then dismiss the modal
[self dismissViewControllerAnimated:YES completion:^{
//Do more cool stuff
}];
}
Все это будет работать с вашим текущим пользовательским кодом перехода, но даст вам больше контроля над тем, что происходит до того, как модальный/оверлей будет уволен. Делегат - прекрасная вещь.
Кроме того, этот код анимации будет отличаться от кода для других функций, что является немного более чистым IMO.
Ответ 5
@Джон Трэсидс решил проблему. Спасибо, Джон!
Но я хотел бы немного расширить ответ.
Если вы представляете экземпляр UIViewController с modalPresentationStyle = .custom
(objc UIModalPresentationCustom), чтобы вызывать методы жизненного цикла viewcontroller, вам необходимо явно управлять представлением viewcontrollers. Для этого просто вызовите beginAppearanceTransition перед анимацией и endAppearanceTransition в блоке завершения анимации.
Также вы можете перейти к своему подклассу UIPresentationController вашего переходного класса аниматора с переопределенным значением shouldRemovePresentersView
, возвращающим true
без вызова beginAppearanceTransition
//Swift 4
поместите это в свой пользовательский класс UIViewControllerAnimatedTransitioning
перед анимацией
fromViewController.beginAppearanceTransition(false, animated: true)
toViewController.beginAppearanceTransition(true, animated: true)
UIView.animate(withDuration: animationDuration, animations: {
// animation logic…
}) { finished in
fromViewController.endAppearanceTransition()
toViewController.endAppearanceTransition()
let transitionSuccess = !transitionContext.transitionWasCancelled
transitionContext.completeTransition(transitionSuccess)
}
// UIPresentationController subclass
class PresentationController: UIPresentationController {
override var shouldRemovePresentersView: Bool { return true }
}