7

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

baxterma
  • 771
  • 1
  • 9
  • 21
  • 1
    I'm having the same issue, did you ever resolve it without resorting to dismissViewControllerAnimated:completion ? – Chris Apr 23 '15 at 00:41
  • 2
    Hey Chris, I did resolve it, but to be honest, I can't really remember how. As you probably noticed, I posted this question a few months ago, and we've since removed the transition. I seem to remember overriding `adaptivePresentationStyle` in the presentationController, and returning `UIModalPresentationOverFullScreen`. I _think_ this has the same effect as setting the `modalPresentationStyle` on the VC, but still allows the custom transition. Apologies I can't be of more help. – baxterma Apr 23 '15 at 15:42
  • Have exact same issue. All good with present segue and the custom present transition. But the unwind segue completely fails. Doesn't dismiss let alone apply the dismiss custom transition. Same as yourself, I can replace the unwind segue with a dismissViewControllerAnimated. That resolves the problem. However it's not ideal as I would prefer to consistently use segues throughout – Max MacLeod Apr 28 '15 at 12:01
  • Looks like the same issue http://stackoverflow.com/questions/25654941/unwind-segue-not-working-in-ios-8/28607309#28607309 – Max MacLeod Apr 28 '15 at 12:34

2 Answers2

1

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")
    }
}
Community
  • 1
  • 1
Kuba Suder
  • 7,126
  • 9
  • 34
  • 39
0

I also met this problem when I need to pass data back from the modalpresented view. I wandering around Google and here for a couple of hours but I couldn't find an answer that is easy to understand for me. But I did get some hint and here's a work around. It seems that because it has to pass data back, and the dismissing process from the automatic Unwind is prior before the data passing which prevented the ViewController being dismissed. So I think that I have to manually dismiss it once one more time.

I got some luck here. I didn't notice that it was a child viewcontroller. I just configured it from the storyboard.

And then in the Unwind function, I added to lines to remove the child viewcontroller and the child view. I have no code in the sourceViewController.

Swift 4.1

@IBAction func unwindToVC(sender :UIStoryboardSegue){

    if let source = sender.source as? CoreLocationVC{
        if source.pinnedCity != nil{
             clCity = source.pinnedCity
        }
        if source.pinnedCountry != nil {
            clCountry = source.pinnedCountry
        }
        if source.pinnedTimeZone != nil {
            clTimeZone = source.pinnedTimeZone
        }
        if source.pinnedLocation != nil {
            clLocation = source.pinnedLocation
        }
        // I added 2 lines here and it just worked
        source.view.removeFromSuperview()
        source.removeFromParentViewController()


    }
William Tong
  • 371
  • 3
  • 12