I want to make an interactive transition between a ViewController (1) and a NavigationViewController (2).
The NavigationController is called by a button, so there's no interactive transition when presenting. It can be dismissed by a button or a UIPanGestureRecognizer, so it can be dismissed interactively or not.
I have an object named TransitionManager for the transition, subclass of UIPercentDrivenInteractiveTransition.
The problem with the code below is that the two delegate methods interactionControllerFor...
are never called.
Moreover, when I press the buttons or swip (UIPanGestureRecognizer), the basic animation of the modal segue is done. So the two delegate methods animationControllerFor...
don't work either.
Any Ideas ? Thanks
ViewController.swift
let transitionManager = TransitionManager()
override func viewDidLoad() {
super.viewDidLoad()
self.transitioningDelegate = transitionManager
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let dest = segue.destinationViewController as UIViewController
dest.transitioningDelegate = transitionManager
dest.modalPresentationStyle = .Custom
}
TransitionManager.swift
class TransitionPushManager: UIPercentDrivenInteractiveTransition,
UINavigationControllerDelegate, UIViewControllerTransitioningDelegate {
@IBOutlet var navigationController: UINavigationController!
var animation : Animator! // Implement UIViewControllerAnimatedTransitioning protocol
override func awakeFromNib() {
var panGesture = UIPanGestureRecognizer(target: self, action: "gestureHandler:")
navigationController.view.addGestureRecognizer(panGesture)
animation = Animator()
}
func gestureHandler(pan : UIPanGestureRecognizer) {
switch pan.state {
case .Began :
interactive = true
navigationController.presentingViewController?.dismissViewControllerAnimated(true, completion:nil)
case .Changed :
...
default :
...
interactive = false
}
}
func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return animation
}
func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return animation
}
func interactionControllerForPresentation(animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
return nil
}
func interactionControllerForDismissal(animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
return self.interactive ? self : nil
}
Main.storyboard
The button on the ViewController triggered a modal segue to presenting the NavigationController
The NavigationController's delegate outlet is linked to an object of the TransitionManager class
The NavigationController is referenced in the TransitionManager class by the property "navigationController"
I think the key problem is that you're configuring the
transitionDelegate
inviewDidLoad
. That's often too late in the process. You should do it as youinit
the navigation controller.Let's imagine your root scene ("Root") that presents the navigation controller scene ("Nav"), that then pushes from scene A to B to C, for example, I'd imagine an object model like this, where the navigation controller would simply own its own animation controller, interaction controller, and gesture recognizer:
This is all you need when considering (a) a custom transition (non-interactive) when "root" presents "nav"; and (b) a custom transition (interactive or not) when "nav" dismisses itself in order to return to the "root". So, I'd subclass the navigation controller which:
adds a gesture recognizer to its view;
sets the
transitioningDelegate
to yield the custom animation as you transition from the root scene to the navigation controller scene (and back):the
transitioningDelegate
will also return the interaction controller (which will only exist while the gesture recognizer is in progress), yielding interactive transition during gesture and non-interactive transition if you dismiss outside of the context of the gesture.In Swift 3, that looks like:
So, I can just change the base class of my navigation controller in the storyboard to be this custom subclass, and now the root scene can just present the navigation controller (with no special
prepare(for:)
) and everything just works.