自定义转换动画在解除时不调用VC生命周期方法

时间:2014-08-25 14:32:11

标签: ios ios7

所以我构建了一个自定义呈现过渡动画,除了视图控制器生命周期方法没有被调用时,一切似乎都很好用。

在呈现控制器之前,我使用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];
    }];
}

6 个答案:

答案 0 :(得分:34)

另一种解决方案可能是使用 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
}

但我仍然认为自定义模态演示没有做到这一点很奇怪。

答案 1 :(得分:13)

如果您使用UIModalPresentationCustom,则应提供自定义UIPresentationController类,如果要使用ViewController生命周期调用者,则需要覆盖shouldRemovePresentersView并返回YES

如果您希望保留演示者并仍然具有ViewControlelr生命周期回调,则可以覆盖私有方法_shouldDisablePresentersAppearanceCallbacks并在自定义NO类中返回UIPresentationController

答案 2 :(得分:8)

经过多次争论,我发现在ios7和ios8中运行的最佳解决方案是保留modalPresentationStyle = UIModalPresentationFullScreen而不是文档建议的UIModalPresentationCustom

如果我这样做以及将transitioningDelegate设置为我的委托,它仍然尊重我的过渡将在'from'视图控制器上调用will / diddisappear方法。另外:没有现在然后旋转然后解雇旋转问题才能启动。

答案 3 :(得分:3)

啊,这是一个模态演示。我不相信使用该方法使用自定义转换调用viewWillAppear和viewDidAppear,因为视图在视图层次结构中在技术上仍处于活动状态。我建议像往常一样使用一个呈现的模态来使用委托。

在显示的VC上创建委托协议。创建一个可以从呈现VC调用的委托方法。在显示叠加层时,将呈现VC设置为委托。然后,从呈现的VC中调用该委托方法。然后,在致电dismissViewController

之前,您可以在演示VC中调用任何类型的操作

在你的叠加层(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文件中:( 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。

答案 4 :(得分:2)

@John Tracids'的anser解决了我的问题。谢谢约翰!

但我想稍微提一下答案。

如果要使用modalPresentationStyle = .custom(objc UIModalPresentationCustom )呈现 UIViewController 实例以保持调用viewcontroller的生命周期方法,则必须明确管理viewcontroller的外观。要做到这一点,只需在动画前调用 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 }
}

答案 5 :(得分:0)

调用viewWillAppear和其他生命周期方法存在相同的问题。 我要解决的问题是实现了委托方法func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController?

然后使其生效,我执行以下操作:

class ViewController: UIViewController {
... 

func showViewController() {
// load your view controller as you want
    guard let detailViewController = loadDetailViewcontroller()  as? DetailViewController else {
            return }
        detailViewController.modalPresentationStyle = .custom
        detailViewController.transitioningDelegate = self
        present(detailViewController, animated: true, completion: nil)
  }
}

extension ViewController: UIViewControllerTransitioningDelegate {
  func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
    return PresentationController(presentedViewController: presented, presenting: presenting)
 }
}

PresentationController类似于演示的临时对象。 苹果的文档

  

从显示视图控制器到关闭它的整个过程,UIKit使用显示控制器来管理该视图控制器的显示过程的各个方面。演示控制器可以在动画对象提供的动画之上添加自己的动画,可以响应大小变化,还可以管理视图控制器在屏幕上的呈现方式的其他方面。