UIViewController transition animation
August 30, 2015

Sometimes we need to spice default UIViewController transition animation a little bit up. Thus we need to write our custom one. Here I will guide you through the process of creating custom UIViewController transition animation.
... in case you don't want to create your own animator object, you can use my customizable class, which will get you exactly that transition animation: ARSSlideTransition.
Creating animator object
Implementing header file and class extension
We are creating Animator class, which is a subclass of
@interface Animator : NSObject <UIViewControllerAnimatedTransitioning>
@property (nonatomic) UINavigationControllerOperation operation;
@end
Also it would be nice to store
@interface Animator ()
@property (weak, nonatomic) id<UIViewControllerContextTransitioning> context;
@end
Animation duration
Next, we need to implement couple of required methods, since we are conforming to this transition protocol. First method is
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext {
return 0.6;
}
Getting and configuring UIViews before animation
Now we are implementing second method, where we are going to prepare our
- First of all we need to save
transitionContext object, then we are going to getUIViewController s that participate in animation - Next we calculate animation direction depending on type of
operation we have. - The very last step in this method - we are moving the view, to which we are going to be transitioning to, offscreen, in order to perform this slide animation of it's containing
UIViews .
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
self.context = transitionContext;
id fromVC = [self.context viewControllerForKey:UITransitionContextFromViewControllerKey];
id toVC = [self.context viewControllerForKey:UITransitionContextToViewControllerKey];
[[self.context containerView] addSubview:[toVC view]];
CGFloat delta = CGRectGetWidth([toVC view].frame);
delta = UINavigationControllerOperationPush == self.operation ? delta : delta * (-1);
[toVC view].frame = [self.context finalFrameForViewController:toVC];
[toVC view].transform = CGAffineTransformMakeTranslation(delta, 0);
[self animationFromViewController:fromVC toViewController:toVC delta:delta];
}
Animation itself
Now we are implementing our helper method, where we are going to encapsulate our animation logic.
- (void)animationFromViewController:(id)fromVC toViewController:(id)toVC delta:(CGFloat)delta {
__block NSTimeInterval timeOffset = self.initialDelay ? self.initialDelay : 0.0;
[UIView animateWithDuration:0.0 delay:0.0 options:0.6 animations:nil completion:^(BOOL finished) {
for (UIView *object in [fromVC objectsToAnimate]) {
[UIView animateWithDuration:0.6 delay:timeOffset usingSpringWithDamping:0.8 initialSpringVelocity:0.8 options:UIViewAnimationOptionCurveEaseOut animations:^{
object.transform = CGAffineTransformMakeTranslation(-delta, 0);
} completion:nil];
timeOffset += 0.05;
}
timeOffset = 0.15;
for (NSInteger i = 0; i < [toVC objectsToAnimate].count; i++) {
UIView *object = [[toVC objectsToAnimate] objectAtIndex:i];
BOOL isLastObject = i == [toVC objectsToAnimate].count - 1;
void(^completionBlock)(BOOL) = !isLastObject ? nil : ^(BOOL finished) {
[self.context completeTransition:YES];
};
[UIView animateWithDuration:0.6 delay:timeOffset usingSpringWithDamping:0.8 initialSpringVelocity:0.8 options:UIViewAnimationOptionCurveEaseOut animations:^{
object.transform = CGAffineTransformMakeTranslation(-delta, 0);
} completion:completionBlock];
timeOffset += 0.05;
}
}];
}
I suppose you are wondering why we need to encapsulate our
Completing animation
When animation completes, we need to position all our
- (void)animationEnded:(BOOL)transitionCompleted {
id viewController = [self.context viewControllerForKey:UITransitionContextToViewControllerKey];
[viewController view].transform = CGAffineTransformIdentity;
for (UIView *object in [viewController objectsToAnimate]) {
object.transform = CGAffineTransformIdentity;
}
}
Configuring presented UIViewController
In
- (NSArray *)objectsToAnimate {
return [self.tableView visibleCells];
}
Configuring presenting UIViewController
In order to tell the system that we are going to use a custom transition, we have to do a couple things:
- conform to
UINavigationControllerDelegate - specify the class you wish to be a
delegate - implement
navigationController:animationControllerForOperation: fromViewController:toViewController: method - implement
objectsToAnimate method, that we are going to call to get array of views we want to animate
@interface ViewController () <UINavigationControllerDelegate>
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationController.delegate = self;
}
- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC
{
Animator *transition = [Animator new];
transition.operation = operation;
return transition;
}
- (NSArray *)objectsToAnimate {
return [self.tableView visibleCells];
}
Conclusion
There you have it, a nice UIViewController transition animation, that is definitely gives some points to your app's coolness. However, you cannot interact with this kind of animation. I will review animations that you can interact with in another article.