Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom unwind segue animation

I have the following set up:

Setup idea

The red controller has a method

- (IBAction)unwindToRed:(UISegue *)segue

and a

- (UIStoryboardSegue *)segueForUnwindingToViewController:(UIViewController *)toViewController fromViewController:(UIViewController *)fromViewController identifier:(NSString *)identifier

which returns a custom segue for this event.

In my custom segue I have the following in the perform code:

- (void)perform
{
    // Just helpers to get the controllers from the segue
    UIViewController *destination = self.destinationViewController;
    UIViewController *source = self.sourceViewController;

    // Setting transitioning delegate for custom transitions
    destination.transitioningDelegate = self;
    source.transitioningDelegate = self;

    destination.modalTransitionStyle = UIModalPresentationCustom;
    source.modalPresentationStyle = UIModalPresentationCurrentContext;

    // When I am transitioning to a new one
    if(!self.reversed)
    {
        [source presentViewController:destination animated:YES completion:nil];
    }
    // Or reversing from a current one
    else
    {
        [source dismissViewControllerAnimated:YES completion:nil];
    }
}

As you can see I have setup a transition delegate which which in turn calls

- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext

on an animation controller where I apply my custom animation. But now when I tap Unwind to Red on the yellow view, it only "unwinds" to its parent. (e.g the blue one).

When I leave the custom segue out of the equation it works just fine. It goes al the way to the top.

Now I think this has to do with the

[source dismissViewControllerAnimated:YES];

line, since that usually just goes "one up" my question is I think, what should I call there so it goes all the way up to red when needed? Yellow is a modal overlay. So "pop too root" would not work.

like image 660
Matthijn Avatar asked Jul 02 '14 12:07

Matthijn


1 Answers

Specifing a Custom Segue

The solution was to use a custom segue for the unwind. Unfortunately you cannot specify a custom segue for an unwind in interface builder like you can on a normal segue. You have to specify it with code.

There is a function on UIViewController that you can override called segueForUnwindingToViewController which specifies which segue to use for the unwind. The tricky part is knowing which view controller to override this function on. The answer is to do it on the parent of the view controllers you are transitioning to/from. In my case it is the UINavigationController. The navigation controller is the container view controller for the first and third view controller. If I had embedded child view controllers in my views, then the answer would not be the navigation controller but the parent view controller.

Create a custom navigation controller by inheriting from UINavigationController. Then make sure to set the custom class of your navigation controller in interface builder. Now you can override the segueForUnwindingToViewController function. Here is a minimal implementation.

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

Adding Animation to Custom Segue

override func segueForUnwindingToViewController(toViewController: UIViewController,
                                                    fromViewController: UIViewController,
                                                    identifier: String?) -> UIStoryboardSegue {
        return UIStoryboardSegue(identifier: identifier, source: fromViewController, destination: toViewController) {
            let fromView = fromViewController.view
            let toView = toViewController.view
            if let containerView = fromView.superview {
                let initialFrame = fromView.frame
                var offscreenRect = initialFrame
                offscreenRect.origin.x -= CGRectGetWidth(initialFrame)
                toView.frame = offscreenRect
                containerView.addSubview(toView)
                // Being explicit with the types NSTimeInterval and CGFloat are important
                // otherwise the swift compiler will complain
                let duration: NSTimeInterval = 1.0
                let delay: NSTimeInterval = 0.0
                let options = UIViewAnimationOptions.CurveEaseInOut
                let damping: CGFloat = 0.5
                let velocity: CGFloat = 4.0
                UIView.animateWithDuration(duration, delay: delay, usingSpringWithDamping: damping,
                        initialSpringVelocity: velocity, options: options, animations: {
                    toView.frame = initialFrame
                }, completion: { finished in
                    toView.removeFromSuperview()
                    if let navController = toViewController.navigationController {
                        navController.popToViewController(toViewController, animated: false)
                    }
                })
            }
        }
    }

Details are from this post

like image 155
AechoLiu Avatar answered Nov 05 '22 03:11

AechoLiu