0

Stumped at this one.

I have a UIViewController subclass that has a panGesture added onto one of its views. This isn't the primary reason for having it subclass UIViewController; it also provides a certain aesthetic for other viewControllers to inherit. The controller conforms to UIViewControllerTransitioningDelegate and sets the transitioningDelegate property on itself as itself upon a successful init. It also then sets the modalPresentationStyle property to be custom.

It provides in the delegate methods, animators for presenting and dismissing and an interactionController for dismissal, which is simply an instance of UIPercentDrivenInteractiveTransition().

This view, by the way, is always presented partially onto the screen. When dismissing with a gesture, everything behaves normally unless the yOffset of the panGesture is a negative value. If the gesture finishes with a negative Y offset, the interaction controller cancels the interactive transition, as expected, but all touches registered by the panGesture afterwards don't reflect visually.

After a bit of printing, I've noticed that the transitioningDelegate doesn't get nilled out, nor does the presentationStyle change. The delegate methods for UIViewControllerTransitioningDelegate stop being called. I can't figure out why that is.

This is called after the custom initialiser and required initialiser with NSCoder.

private func setup() {

    transitioningDelegate = self
    modalPresentationStyle = .Custom
}

Inside of the viewDidLoad(), the following happens.

panGesture.addTarget(self, action: "handlePan:")
panGesture.delegate = self
view.addGestureRecognizer(panGesture)

The handling of the pan looks something like this:

    switch gesture.state {

    case .Began :

        interactionController = UIPercentDrivenInteractiveTransition()
        dismissViewControllerAnimated(true, completion: nil)

    case .Changed:

        interactionController.updateInteractiveTransition(percentage)

    case .Cancelled, .Failed, .Ended:

        if (gesture.velocityInView(view).y < 0) {

            interactionController.cancelInteractiveTransition()
            if gesture.translationInView(view).y < 0 {

                print("This is when things go weird.")
            }

        }
        else {

            interactionController.finishInteractiveTransition()
        }

    default:

        let _ = 0
    }

And the UIViewControllerTransitioningDelegate() delegate method for the interactionControllerDismissal simply returns the variable like the this:

func interactionControllerForDismissal(animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {

    return interactionController
}

Am I presenting the viewController incorrectly by having it only partially on the screen? Is this the correct way to have it on the screen?

  • Usually the `interactionControllerForDismissal` returns a new instance dedicated to dismissing explicitly, not for cancellation. It's been a few years since I touched this stuff, so I can't remember exactly what the process is. Maybe this guy knows? http://www.teehanlax.com/blog/custom-uiviewcontroller-transitions/ – Ash Furrow Oct 06 '15 at 10:11
  • The subsequent delegate functions don't get called if the panGesture finishes with the finger outside of the bounds of the viewController view. – Rehat Kathuria Oct 06 '15 at 10:32
  • Possibly unrelated, but in your `.Cancelled, .Failed, .Ended` case, you should `nil` the `interactionController` (in case you start and cancel a gesture, but then subsequently perform some other action that needs a non-interactive gesture, but `interactionControllerForDismissal` will return the old, cancelled interaction controller). But, setting that aside, I read the question and am unclear as to what the problem is. Also, I'm unclear (a) why you're checking `translationInView` _after_ it's already been cancelled; and (b) what precisely is happening (i.e. what does "weird" mean). – Rob Mar 21 '16 at 18:38

0 Answers0