Подтвердить что ты не робот

Пользовательская анимация перехода, не вызывающая методы жизненного цикла 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];
    }];
}
4b9b3361

Ответ 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 }
}