Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unwind Segue not dismissing View Controller with UIModalPresentationCustom

I'm presenting a modal view controller using a custom transition (by setting its modelPresentationStyle to UIModalPresentationCustom, providing a transitioning delegate, and UIViewControllerAnimatedTransitioning object).

In the presented view controller, I have an unwind segue hooked up to a button. The segue fires just fine; the IBAction method in the presenting view controller is called, and so is prepareForSegue in the presented view controller. However, the presented view controller is not dismissed, and the appropriate transitioning delegate method (animationControllerForDismissedController:) is not called.

If, however, I set the presented view controller's modalPresentationStyle to UIModalPresentationFullScreen (the default), the view controller is dismissed properly (this breaks my custom transition, though).

I'm at a complete loss at what to do here. I've looked through Apple's documentation, and didn't notice anything saying that one had to do special things with unwind segues when dealing with custom transitions.

I'm aware that I could call dismissViewControllerAnimated:completion: in the IBAction method of the presenting view controller, but I'd rather use that as a last resort, and get the unwind segue working the way it should (or at least know why it's not working :) ).

Any help would be much appreciated,

Thanks in advance

like image 234
baxterma Avatar asked Feb 25 '15 12:02

baxterma


Video Answer


1 Answers

It seems that if you use UIModalPresentationCustom to present the controller with a custom transition manager, you also need to use a custom transition manager to dismiss it (which makes sense I guess, since you can do all kinds of weird stuff in the animator object and UIKit can't be sure that just dismissing the screen as usual will completely restore the original state - I just wish it told you that explicitly...).

Here's what I've done to fix this in my app:

  • override segueForUnwindingToViewController in the parent view controller (the one to which you're moving after the dismiss animation) and return an instance of your UIStoryboardSegue, either the one you've used for the original transition or a new separate class
  • if the unwind segue's target view controller is in a navigation hierarchy, then you need to override that method in the navigation controller instead
  • in the perform method call dismissViewControllerAnimated
  • the presented view controller needs to still hold a valid reference to the transitioning delegate, or you'll get an EXC_BAD_ACCESS (see DismissViewControllerAnimated EXC_Bad_ACCESS on true) - so either make it keep the delegate as a strong reference as described in that thread, or assign a new one before calling dismissViewControllerAnimated (it's possible that changing modelPresentationStyle to e.g. full screen before dismissing would work too, but I haven't tried that)
  • if the dismiss animation needs to do any non-standard things (mine luckily didn't), override animationControllerForDismissedController in the transition manager object and return a proper animator
  • if the target view controller is in a navigation hierarchy, then you also need to manually pop the navigation stack to the target controller before dismissing the presented screen (i.e. target.navigationController!.popToViewController(target, animated: false))

Complete code sample:

// custom navigation controller 

override func segueForUnwindingToViewController(toViewController: UIViewController,
fromViewController: UIViewController,
identifier: String?) -> UIStoryboardSegue {
    return CustomSegue(
        identifier: identifier,
        source: fromViewController,
        destination: toViewController
    )
}


// presented VC

var customTransitionManager: UIViewControllerTransitioningDelegate?


// custom segue

override func perform() {
    let source = sourceViewController as! UIViewController

    if let target = destinationViewController as? PresentedViewController {
        let transitionManager = TransitionManager()
        target.modalPresentationStyle = .Custom
        target.customTransitionManager = transitionManager
        target.transitioningDelegate = transitionManager

        source.presentViewController(target, animated: true, completion: nil)
    } else if let target = destinationViewController as? WelcomeViewController {
        target.navigationController!.popToViewController(target, animated: false)
        target.dismissViewControllerAnimated(true, completion: nil)
    } else {
        NSLog("Error: segue executed with unexpected view controllers")
    }
}
like image 61
Kuba Suder Avatar answered Nov 01 '22 02:11

Kuba Suder