3

I keep getting the error:

*** Terminating app due to uncaught exception 'NSGenericException', reason: 'Push segues can only be used when the source controller is managed by an instance of UINavigationController.'

When trying to switch to a new view controller. Below is the segue switching view controllers:

image

Even when I try put the name of my transition class in the Segue class, it still gives the error on my device, but works perfectly fine in the simulator.

The code for the transition class:

class TransitionManager: UIStoryboardSegue, UIViewControllerAnimatedTransitioning, UIViewControllerTransitioningDelegate  {

private var presenting = true

// MARK: UIViewControllerAnimatedTransitioning protocol methods

// animate a change from one viewcontroller to another
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {

    // get reference to our fromView, toView and the container view that we should perform the transition in
    let container = transitionContext.containerView()
    let fromView = transitionContext.viewForKey(UITransitionContextFromViewKey)!
    let toView = transitionContext.viewForKey(UITransitionContextToViewKey)!

    // set up from 2D transforms that we'll use in the animation
    let π : CGFloat = 3.14159265359

    let offScreenRight = CGAffineTransformMakeRotation(-π/2)
    let offScreenLeft = CGAffineTransformMakeRotation(π/2)

    // prepare the toView for the animation
    toView.transform = self.presenting ? offScreenRight : offScreenLeft

    // set the anchor point so that rotations happen from the top-left corner
    toView.layer.anchorPoint = CGPoint(x:0, y:0)
    fromView.layer.anchorPoint = CGPoint(x:0, y:0)

    // updating the anchor point also moves the position to we have to move the center position to the top-left to compensate
    toView.layer.position = CGPoint(x:0, y:0)
    fromView.layer.position = CGPoint(x:0, y:0)

    // add the both views to our view controller
    container.addSubview(toView)
    container.addSubview(fromView)

    // get the duration of the animation
    // DON'T just type '0.5s' -- the reason why won't make sense until the next post
    // but for now it's important to just follow this approach
    let duration = self.transitionDuration(transitionContext)

    // perform the animation!
    // for this example, just slid both fromView and toView to the left at the same time
    // meaning fromView is pushed off the screen and toView slides into view
    // we also use the block animation usingSpringWithDamping for a little bounce
    UIView.animateWithDuration(duration, delay: 0.0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.8, options: nil, animations: {

        // slide fromView off either the left or right edge of the screen
        // depending if we're presenting or dismissing this view
        fromView.transform = self.presenting ? offScreenLeft : offScreenRight
        toView.transform = CGAffineTransformIdentity

        }, completion: { finished in

            // tell our transitionContext object that we've finished animating
            transitionContext.completeTransition(true)

    })

}

// return how many seconds the transiton animation will take
func transitionDuration(transitionContext: UIViewControllerContextTransitioning) -> NSTimeInterval {
    return 0.75
}

// MARK: UIViewControllerTransitioningDelegate protocol methods

// return the animataor when presenting a viewcontroller
// remmeber that an animator (or animation controller) is any object that aheres to the UIViewControllerAnimatedTransitioning protocol
func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {

    // these methods are the perfect place to set our `presenting` flag to either true or false - voila!
    self.presenting = true
    return self
}

// return the animator used when dismissing from a viewcontroller
func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    self.presenting = false
    return self
}

override func perform() {
    //
}

}

Is there anything I am missing? Or how can I get this to work?

1 Answers1

2

Usually that error indicates that you're trying to perform a push segue, and the presenting view controller is not managed by a UINavigationController. The simple solution is usually to embed the presenting (i.e. from) view controller in a navigation controller as outlined in NSGenericException', reason: 'Push segues can only be used when the source controller is managed by an instance of UINavigationController.

But since you've rolled your own UIStoryboardSegue class, I suspect you aren't trying to simply use the default push segue animation. Without knowing how you are managing your transitioningDelegate, my guess is you need to actually override perform() to present your view controller instead of pushing it:

override func perform() {
    let fromVC  = sourceViewController as UIViewController
    let toVC    = destinationViewController as UIViewController

    fromVC.presentViewController(toVC as UIViewController, animated: true, completion: nil)
}
Community
  • 1
  • 1
rainypixels
  • 587
  • 4
  • 12