Sunday, March 4, 2012

Custom Transition Animation for Modal View Controllers

Needed to implement a custom animation (push from right/left when presenting a modal view controller, and then pushing out to the other direction when dismissing the dialog).
Yosi found this snippet: http://blog.radi.ws/post/5924267283/custom-modal-uiviewcontroller-transitions
by Evadne Wu. This was so cool, and I think I made it a bit more fun by creating a UIViewController category to do that (I was a bit lazy, so I only implemented the Push in/out transition, but this can be easily extended):


//
//  UIViewController+Transitions.h
//  Labgoo Misc
//
//  Created by Israel Roth on 3/4/12.
//  Copyright (c) 2012 Labgoo LTD. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface UIViewController(Transitions)

- (void) presentModalViewController:(UIViewController *)modalViewController withPushDirection: (NSString *) direction;
- (void) dismissModalViewControllerWithPushDirection:(NSString *) direction;

@end


and the implementation:

//
//  UiViewController+Transitions.m
//  Labgoo Misc
//
//  Created by Israel Roth on 3/4/12.
//  Copyright (c) 2012 Labgoo LTD. All rights reserved.
//

#import "UIViewController+Transitions.h"

@implementation UIViewController(Transitions)

- (void) presentModalViewController:(UIViewController *)modalViewController withPushDirection: (NSString *) direction {

    [CATransaction begin];
    
    CATransition *transition = [CATransition animation];
    transition.type = kCATransitionPush;
    transition.subtype = direction;
    transition.duration = 0.25f;
    transition.fillMode = kCAFillModeForwards;
    transition.removedOnCompletion = YES;
    
    [[UIApplication sharedApplication].keyWindow.layer addAnimation:transition forKey:@"transition"];        
    [[UIApplication sharedApplication] beginIgnoringInteractionEvents];
    [CATransaction setCompletionBlock: ^ {
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(transition.duration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^ {
            [[UIApplication sharedApplication] endIgnoringInteractionEvents];        
        });
    }];
    
    [self presentModalViewController:modalViewController animated:NO];
    
    [CATransaction commit];

}

- (void) dismissModalViewControllerWithPushDirection:(NSString *) direction {

    [CATransaction begin];
    
    CATransition *transition = [CATransition animation];
    transition.type = kCATransitionPush;
    transition.subtype = direction;
    transition.duration = 0.25f;
    transition.fillMode = kCAFillModeForwards;
    transition.removedOnCompletion = YES;

    [[UIApplication sharedApplication].keyWindow.layer addAnimation:transition forKey:@"transition"];        
    [[UIApplication sharedApplication] beginIgnoringInteractionEvents];
    [CATransaction setCompletionBlock: ^ {
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(transition.duration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^ {
            [[UIApplication sharedApplication] endIgnoringInteractionEvents];        
        });
    }];
    
    [self dismissModalViewControllerAnimated:NO];
    
    [CATransaction commit];
    
}

@end



to use it, you import "UIViewController+Transitions.h" in your file and call the presentModalViewController:withPushDirection:
the direction parameter receives core animation transition sub-types such as kCATransitionFromRight

Enjoy...

14 comments:

  1. Thanks for putting this up! This might also be improved using CALayer renderInContext:, as mentioned here: http://blog.radi.ws/post/5924267283/custom-modal-uiviewcontroller-transitions ...I look forward to trying this out!

    ReplyDelete
  2. thanks!!!!!
    for noobies like me you missed to import #import on .m

    ReplyDelete
  3. Hi Isreal,

    Great post and exactly what I am looking for. I am trying to do more creative transitions between View Controllers and this is great.

    However, I get the errors below when I try this. I have imported QuartzCore and named this the same name as yours. I haven't included this in any files yet. These errors just happen when I have the UIViewContrllers+Transitions.h files in my project. I am using 4.3.1 and Deployment Target is 5.0.

    Any suggestions? Do I need to include the CATransations reference anywhere?


    Undefined symbols for architecture i386:
    "_OBJC_CLASS_$_CATransaction", referenced from:
    objc-class-ref in UIViewController+Transitions.o
    "_OBJC_CLASS_$_CATransition", referenced from:
    objc-class-ref in UIViewController+Transitions.o
    "_kCAFillModeForwards", referenced from:
    -[UIViewController(Transitions) presentModalViewController:withPushDirection:] in UIViewController+Transitions.o
    -[UIViewController(Transitions) dismissModalViewControllerWithPushDirection:] in UIViewController+Transitions.o
    "_kCATransitionPush", referenced from:
    -[UIViewController(Transitions) presentModalViewController:withPushDirection:] in UIViewController+Transitions.o
    -[UIViewController(Transitions) dismissModalViewControllerWithPushDirection:] in UIViewController+Transitions.o
    ld: symbol(s) not found for architecture i386
    clang: error: linker command failed with exit code 1 (use -v to see invocation)

    ReplyDelete
    Replies
    1. Add QuartzCore.Framework to your targets

      Delete
  4. Hi Isreal,

    Figured it out. My files were showing QuartzCore but, it wasn't in my build settings. I added it and now it works. However...

    The kCATransitionFrom... is working in the from order. When I say kCATransitionFromTop it comes from the right. When I say kCATransitionFromRight it comes from the Top. Same with left and bottom, they are reversed. Not sure what to make of that but, this was a huge help to me.

    Many Thanks.
    kt

    ReplyDelete
  5. hi there, I tried your method with some tweak and ended up with this problem. Do you have any suggestion? http://stackoverflow.com/q/9842949/581194

    ReplyDelete
  6. Thanks very much for this Srool. It really helped me out a lot.

    ReplyDelete
  7. thanks for the feedback and appologies for not seeing your comments earlier - was so busy programming that I failed to post

    ReplyDelete
  8. Thank you so much for this! This is exactly what I was looking for and it helped a ton!

    ReplyDelete
  9. Thank you for this.
    Your copyright says "All rights reserved."
    Is there a problem with me incorporating this in a commercial product?

    ReplyDelete
  10. Thank you for this - it helped us out a LOT.
    I noticed that your Copyright statement says "All rights reserved." Is there a problem if we use this code within our commercial product?

    ReplyDelete
  11. (OpenID seems to be broken.)

    Thank you for this - it's helped us out in our App.

    Your copyright notice in the code says "All rights reserved." We'd like to use this code in a commercial product; is there a problem with that?

    ReplyDelete
    Replies
    1. no problem. sorry for not responding earlier.

      Delete
  12. thnx buddy it helps me a lot.. :)

    ReplyDelete