Custom transition animation not calling VC lifecycle methods on dismiss

后端 未结 6 1500
梦谈多话
梦谈多话 2020-12-24 11:11

So I built a custom presenting transition animation and everything seems to be working great except the view controller lifecycle methods are not being called on dismiss.

相关标签:
6条回答
  • 2020-12-24 11:52

    @John Tracids' anser solved my issue. Thanks John!

    But I would like to extend an answer a bit.

    If you are presenting UIViewController instance with modalPresentationStyle = .custom (objc UIModalPresentationCustom) in order to keep viewcontroller's lifecycle methods being called, you have to manage viewcontroller’s appearance explicitly. To do that just call beginAppearanceTransition before animation and endAppearanceTransition at the animation completion block.

    Also you can pass to your transitioning animator class custom UIPresentationController subclass with overridden value shouldRemovePresentersView returning true without calling beginAppearanceTransition

    // Swift 4

    put this to your custom UIViewControllerAnimatedTransitioning class before animation

    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 }
    }
    
    0 讨论(0)
  • 2020-12-24 11:55

    If you are using UIModalPresentationCustom you should provide custom UIPresentationController class, and if you want to use ViewController lifecycle callers, you need to override shouldRemovePresentersView and return YES.

    If you would like to keep presenters and still have ViewControlelr lifecycle callback, you can override private method _shouldDisablePresentersAppearanceCallbacks and return NO in your custom UIPresentationController class.

    0 讨论(0)
  • 2020-12-24 11:56

    Ah, this is a modal presentation. I don't believe viewWillAppear and viewDidAppear are called with custom transition using the method, as the view is technically still active in the view hierarchy. I'd suggest using delegation here as you normally would with a presented modal.

    Create delegate protocol on the presented VC. Create a delegate method that can be called from the presenting VC. As you present the overlay, set the presenting VC as the delegate. Then, call that delegate method from the presented VC. Then you can call any sort of actions from within the presenting VC before you call dismissViewController

    In your overlay (ModalViewController.h):

    @protocol ModalViewDelegate <NSObject>
    -(void)didDismissModalView;
    @end
    
    @interface ModalViewController : UIViewController
    @property(strong, nonatomic) id <ModalViewDelegate> delegate;
    

    In your ModalViewController.m, call a method that calls your delegate method:

    - (void)dismissModal{
        [self.delegate didDismissModalView];
    }
    

    In your presenting VC h file: (PresentingViewController.h), make this class conform to your modal delegate protocol:

    @interface PresentingViewController : UIViewController <ModalViewDelegate>
    

    In your presenting VC, as you present the modal:

    ...
    ModalViewController *modalViewController = [[ModalViewController alloc] init];
    modalViewController.delegate = self; //the makes the presenting VC the delegate
    [self presentViewController:modalViewController animated:YES completion:nil];
    ...
    

    Finally, in your presenting VC, when you want to perform some actions before dismissing the modal, implement the ModalViewDelegate method:

    - (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
        }];
    }
    

    All of this will work with your current custom transition code, but will give you more control over what happens before the modal/overlay is dismissed. Delegate is a beautiful thing.

    Further, this will keep this animation code separate from code for other functionality, which is a bit cleaner IMO.

    0 讨论(0)
  • 2020-12-24 11:57

    After much wrangling with this issue, I found the best solution which works in ios7 and ios8 is to leave modalPresentationStyle = UIModalPresentationFullScreen instead of UIModalPresentationCustom as the docs suggest.

    If i do this as well as setting the transitioningDelegate to my delegate, it still respects my transition and the will/diddisappear methods get called on the 'from' view controller. Also: no present-then-rotate-then-dismiss rotation issues to boot.

    0 讨论(0)
  • 2020-12-24 12:05

    I have the same problem to call viewWillAppear and other lifecycle methods. What I did to solve it was implemented the delegate method func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController?

    Then to make it work I do the following:

    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)
     }
    }
    

    The PresentationController is like a temporary object for the presentation. Apple's documentation

    From the time a view controller is presented until the time it is dismissed, UIKit uses a presentation controller to manage various aspects of the presentation process for that view controller. The presentation controller can add its own animations on top of those provided by animator objects, it can respond to size changes, and it can manage other aspects of how the view controller is presented onscreen.

    0 讨论(0)
  • 2020-12-24 12:17

    Another solution could be using beginAppearanceTransition: and endAppearanceTransition:. According to documentation:

    If you are implementing a custom container controller, use this method to tell the child that its views are about to appear or disappear. Do not invoke viewWillAppear:, viewWillDisappear:, viewDidAppear:, or viewDidDisappear: directly.

    Here is how I used them:

    - (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
    }
    

    But I still consider strange that custom modal presentation not doing this.

    0 讨论(0)
提交回复
热议问题