Creating Animated Custom and Unwind Segues in iOS

Segues

I think most iOS developers know by now how to create a segue from one View Controller (VC) to another. A segue is the transition between one View Controller and another that occurs when you press a button, or press on a row in a table. It’s easy enough to create one in Interface Builder simply by Ctrl-dragging from the source object to the target View Controller. The options for the segue are “Push”, “Modal” and “Custom”.

Push Segue
A Push segue is used when you’re using a Navigation Controller. The first VC is placed on the navigation stack and the Second View Controller slides into view. When you press the “Back” button the Second VC is dismissed and the first VC is “popped” off the stack and is presented again.

Modal Segue
A “Modal” segue creates a relationship between the VC that did the presenting and the VC that was presented. Put simply, when a modal segue occurs, the target VC is presented in front of the source VC and it’s the responsibility of the presented VC to dismiss itself at some point, perhaps when triggered by pressing a “Done” or “Cancel” button.

Transitions

On an iPhone, there are four types of transitions available:

Cover Vertical (default)
Used to interrupt the current workflow to gather information from the user. The presented VC should provide buttons to dismiss itself by means of a “Done” button and an optional Cancel button.

Flip Horizontal
Used to temporarily change the work mode of the app. For example, to display settings as in the Stocks and Weather apps. Usually requires a button to return the user to the previous state.

Cross Dissolve
Used to present an alternate interface when the device changes orientations. The app is responsible for presenting and dismissing the alternate VC in response to orientation change notifications.

Partial Curl
Used to curl up one corner of the current VC to reveal a partial view on the presented VC. This transition was used in the Maps app on iOS 6 to reveal settings.

The first three of these stock transitions are fine for the prescribed cases where you want to adhere to Apple’s UI guidelines, but I’m not sure if the Partial Curl transition style fits in with the new flat design philosophy of iOS 7. We’re beginning to see different types of transitions in iOS 7 where views seem to “fly in” and “fly out”, and, in fact, Apple have introduced a new API using the UIViewControllerAnimatedTransitioning protocol to manage these animations. This will be the subject of a future blog post.

Custom Segue

In this post I want to show how to create an expanding and contracting animated transition using the third segue type, “Custom”. In this transition, pressing a button in the first View Controller will initiate an animation effect where the target View Controller expands and grows from the button to become a full screen view. Once the target VC is displayed, pressing another button will show an animation of the view shrinking back to the original button. This latter transition will use an “Unwind Segue”, a type of segue that reverses back to the original VC.

The example Xcode project is hosted at GitHub and you can either clone it or download it from here:

https://github.com/Phillipus/CustomSegue

(Note that I’ve set up this project to run only on iOS 7. This makes it easier to work with. You’ll therefore need to be using Xcode 5.)

Load the example project in Xcode and run it in the iOS simulator. When you see the first screen press the “Segue Segue Sputnik!” button:

First View Controller

First View Controller

This will animate to the next screen, the Second View Controller:

Second View Controller

Second View Controller

Now press the “Get Back!” button. Notice how the screen shrinks back to the original button. Quite a nice effect. Let’s see how it’s done.

First of all, let’s take a look at how we create the forward segue.

In Xcode select the “Main.storyboard” file so that you can view it in the Storyboard editor. Then select the segue (arrow) that goes from the left View Controller to the right View Controller so that you can view its properties in the Inspector:

Storyboard

Storyboard

To create the segue I simply Ctrl-dragged from the button in the left VC to the right VC. Xcode created the segue for me, and all I had to do was edit the details in the Inspector. If you look in the Inspector, you can see that I’ve given the segue an identifier name of “CustomSegue”, chosen the style “Custom”, and referenced the class, “CustomSegue”. So what does the CustomSegue class actually do? Let’s take a look at it:

Our Custom segue is an instance of UIStoryBoardSegue. This makes sense as we’re using a segue as created in a Storyboard. There’s only one method that we need to over-ride, the perform method, and this will be called when the segue occurs. As the UIStoryBoardSegue instance contains two properties for the source and target View Controllers we can grab references to those in order to create the animated transition between the two. The rest of the code adds the destination’s view to the source view as a sub-view temporarily so we can see it and create the animation effect. This consists of shrinking the view to a small size, setting its centre point to the originating point (as we’ll see later this is set to be the button that we press) and animating it back to its original size and centre point with a call to [UIView animateWithDuration]. When the animation completes in the completion block, we remove the destination view from its parent super view and then present the destination VC.

Note that our CustomSegue class has a property, originatingPoint, declared in the header file:

The custom segue needs to know the originating point from which to start the animation. This is the “Segue Segue Sputnik” button in the first VC. This is set in FirstViewController.m:

The method, prepareForSegue:, is invoked just before the segue occurs. This gives us an opportunity to set things up, in our case by checking that this is the right kind of segue (we could have more than one) and to set the CustomSegue‘s originatingPoint to the centre of the button. Once this is done, the segue occurs.

Unwind Custom Segue

Now that we’ve examined how to create a segue going forward, let’s look at how we get back from the second View Controller to the first View Controller. This is done by creating an “unwind segue”. But before we can create one in the Storyboard, there’s one thing we need to do first. An unwind segue requires that you provide an IBAction method in the View Controller that you want to unwind to. This method can have any name but it needs to have the following method signature:

We’ve implemented this in FirstViewController. We don’t actually do anything when this is invoked, but we still need to provide it in order to form the link in the Storyboard, as we shall see.

Once we’ve done that, it’s easy enough to create the unwind segue in the Storyboard by Ctrl-dragging from the “Get Back!” button in the second VC to the green “Exit” button:

Making the Unwind Segue

Making the Unwind Segue

When you release the mouse-button, you’ll see the method name and you can link the connection to it:

Linking to the method

Linking to the method

Now you’ve created the “glue” between the second View Controller and the first View Controller, you need to provide an instance of the UIStoryboardSegue that will be used to perform the unwind. When we created the forward segue earlier by Ctrl-dragging from the “Segue Segue Sputnik!” button to the second VC we could specify the identifier and class of the custom segue in the Inspector. With unwind segues we can’t do this, instead we have to instantiate one in code. We can do this in the FirstViewController class by over-riding the segueForUnwindingToViewController: method that’s declared in UIViewController. Here’s our implementation:

Here we instantiate a new instance of CustomUnwindSegue with the parameters that are passed from the segueForUnwindingToViewController: method and then set the target point that the animation will shrink the view to, in fact it’s the inverse of what we did earlier. If we look at the code for the CustomUnwindSegue class, we can see that it pretty much reverses what we did in the CustomSegue class:

When the animation completes in the completion block we remove the animated view from its parent (as we did earlier) and then dismiss the VC, thus revealing the FirstViewController.

Wrap up

And that’s about it. Please note that you’ll need to target a minimum of iOS 6 in order to work with unwind segues. I think that this is a useful basic pattern for implementing custom segues and unwind segues. Once you’ve established this in your application you can then go on to experiment with different animation techniques as you transition from one VC to the next. As I wrote earlier, iOS 7 introduces some new API to handle animated transitions, and I’ll look at this in a future post.

Share this...Tweet about this on TwitterShare on LinkedInShare on Facebook

18 thoughts on “Creating Animated Custom and Unwind Segues in iOS

    • No, it doesn’t. This is not meant to be used for a Navigation Controller which has its own mechanism for popping the previous VC off the stack and transitioning back.

      • But what if you wanted to have a custom animation to unwind? Also, I noticed when using a custom segue the navigation controller is lost in the child.

        Any thoughts?

        • You should not use custom seques with Navigation View Controllers. This breaks the expected UI experience. The user expects to slide back to the previous view.

  1. Hi. Unwind need something. It does not dismiss ViewController between. If VC1 > VC2 > VC3, then unwind from VC3 to VC1, VC2 is not dismissed. It will show up VC2 in the end. ?

      • no. with modal. I’m not using navigation bar for this project.
        I found a way. but untidy. with notification. I can’t find other way.

        unwindSegue.h
        [code]@property (nonatomic) BOOL dismissVCBetween;[/code]

        unwindSegue.m
        [code]if (self.dismissVCBetween == YES) {
        [[NSNotificationCenter defaultCenter]postNotificationName:@"dismissVCBetween" object:self]; }[/code]

        vcDestination.m
        [code]- (UIStoryboardSegue *)segueForUnwindingToViewController:(UIViewController *)toViewController fromViewController:(UIViewController *)fromViewController identifier:(NSString *)identifier {
        WISUnwindSegue *segue = [[WISUnwindSegue alloc] initWithIdentifier:identifier source:fromViewController destination:toViewController];

        if ([identifier isEqualToString:@"unwindHereAndDismissVcToDissmis:"]) {
        segue.dismissVCBetween = 1; }
        return segue; }[/code]

        vcToDismiss.m
        [code]-(void)receiveNotification {
        [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(dismissVCBetween:) name:@"dismissVCBetween" object:nil]; }

        – (void)dismissVCBetween:(NSNotification *)notification {
        [self dismissViewControllerAnimated:NO completion:nil]; }[/code]

  2. unwindSegue.m code wrote in completion block. after
    [code][sourceViewController dismissViewControllerAnimated:NO completion:Nil];[/code]
    it’s important to do in order.

  3. The only issue with this approach is that viewWillAppear: and viewDidAppear are called twice on the destinationViewController. Any ideas of how you could work around this?

      • I put this in SecondViewController.m of your CustomSegue github project:

        -(void)viewWillAppear:(BOOL)animated{
        [super viewWillAppear:animated];
        NSLog(@”%s”, __PRETTY_FUNCTION__);
        }

        -(void)viewDidAppear:(BOOL)animated{
        [super viewDidAppear:animated];
        NSLog(@”%s”, __PRETTY_FUNCTION__);
        }

        -(void)viewWillDisappear:(BOOL)animated{
        [super viewWillDisappear:animated];
        NSLog(@”%s”, __PRETTY_FUNCTION__);
        }

        -(void)viewDidDisappear:(BOOL)animated{
        [super viewDidDisappear:animated];
        NSLog(@”%s”, __PRETTY_FUNCTION__);
        }

        The issue is that the view of the second view controller gets added here:

             [sourceViewController.view.superview insertSubview:destinationViewController.view atIndex:0];

        Then removed here:
                                 [destinationViewController.view removeFromSuperview]; // remove from temp super view

        Then added again here (from a behind the scenes perspective):
                                 [sourceViewController dismissViewControllerAnimated:NO completion:NULL]; // dismiss VC

  4. I copied from the unwinding portion of the tutorial by mistake. However, it is the same issue except that instead of dismissing, it is presenting.

    • I guess it’s a side effect of adding and removing. Knowing this it would be possible to flag these events. Use this technique as a starting point. I shall look at a better way of doing this using the iOS 7 API.

      • To be honest, I know how to do this in iOS 7 using the new view controller transitioning delegates. Though you follow many of the same procedures as the ones you have marked out in this tutorial, yet it DOESN’T cause the viewWillAppear…etc. to be called more than once. The reason I came back to your tutorial is that I have a situation where I need to unwind through 4 view controllers back to the starting one, and I need it to be a custom animation. I know you say in the comments above that this shouldn’t be done, but I would have to disagree with that, as there are cases when the user is being prompted to return to the starting point and thus it has it’s place. The problem is, this doesn’t seem to be possible with the new iOS 7 methods, but with a little modification of your unwinding segue example, it is possible. I guess for now I will have to live with the double calls to those methods.

  5. Hei I found (with help reading some stackoverflow post, I forgot the source) super simple solution to jump back several viewController. I had tested it in my app, it works.

    On the unwindSegue.m completion,
    instead using:
    [sourceViewController dismissViewControllerAnimated:NO completion:NULL];
    use:
    [destinationViewController dismissViewControllerAnimated:NO completion:NULL];

    I think it had relation with 2nd paragraph on UIViewController-dismissViewControllerAnimated:Completion reference:

    “Discussion
    The presenting view controller is responsible for dismissing the view controller it presented. If you call this method on the presented view controller itself, it automatically forwards the message to the presenting view controller.

    If you present several view controllers in succession, thus building a stack of presented view controllers, calling this method on a view controller lower in the stack dismisses its immediate child view controller and all view controllers above that child on the stack. When this happens, only the top-most view is dismissed in an animated fashion; any intermediate view controllers are simply removed from the stack. The top-most view is dismissed using its modal transition style, which may differ from the styles used by other view controllers lower in the stack.

    If you want to retain a reference to the receiver’s presented view controller, get the value in the presentedViewController property before calling this method.

    The completion handler is called after the viewDidDisappear: method is called on the presented view controller.”

  6. Hi Phillipus,
    great example.
    could you give me some tips?
    I am trying to make a segue animation that is coming from left to right straight and another one from top to bottom
    (so you can swipe in each direction and the animation will do the same)
    but am having difficulties programming this.

    thanks!

Comments are closed.