iOS7から追加されたUIViewControllerAnimatedTransitioningとUIViewControllerTransitioningDelegateの2つのプロトコルを使用して画面遷移のアニメーションをカスタムする方法を紹介します。
このようにいつものスライドして遷移するのではなく、好きなアニメーションで画面を遷移させることができます。
ViewController.m
#import "ViewController.h"
#import "AnimationController.h"
@interface ViewController () <UIViewControllerTransitioningDelegate, UINavigationControllerDelegate>
{
AnimationController *_animationController;
}
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationController.delegate = self;
// アニメーションを管理するクラス
_animationController =[[AnimationController alloc] init];
}
#pragma mark - UIViewControllerTransitioningDelegate
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
{
return _animationController;
}
#pragma mark - UINavigationControllerDelegate
- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC
{
// 画面遷移の状態によってアニメーションの向きを変える
_animationController.isReverse = operation == UINavigationControllerOperationPop;
return _animationController;
}
@end
遷移元のクラスに以下を実装します。
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed;ここにこれから作成するアニメーションクラスのインスタンスを返します。
- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC;画面が切り替わった時のnavigationControllerの動作が返ってくるので、状態に合わせてアニメーションクラスに値を設定しています。
次にアニメーションクラスを作成します。
AnimationController.h
#import@interface AnimationController : NSObject <UIViewControllerAnimatedTransitioning> @property (nonatomic, assign) BOOL isReverse; @property (nonatomic, assign) NSTimeInterval duration; @end
AnimationController.m
#import "AnimationController.h"
@interface AnimationController() <UIViewControllerAnimatedTransitioning>
@end
@implementation AnimationController
- (id)init
{
if (self = [super init])
{
// アニメーションの時間
self.duration = 1.0f;
}
return self;
}
#pragma mark - UIViewControllerAnimatedTransitioning
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
{
return self.duration;
}
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
[self animateTransition:transitionContext fromView:fromVC.view toView:toVC.view];
}
#pragma mark - animation
// アニメーションを作る
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext fromView:(UIView *)fromView toView:(UIView *)toView
{
UIView* containerView = [transitionContext containerView];
[containerView addSubview:toView];
if (!self.isReverse) [containerView sendSubviewToBack:toView];
CATransform3D transform = CATransform3DIdentity;
transform.m34 = -0.002;
[containerView.layer setSublayerTransform:transform];
UIView *flippedSectionOfView = self.isReverse ? toView : fromView;
if (self.isReverse) flippedSectionOfView.frame = CGRectMake(0, CGRectGetHeight(flippedSectionOfView.frame)*2, flippedSectionOfView.frame.size.height, flippedSectionOfView.frame.size.width);
NSTimeInterval duration = [self transitionDuration:transitionContext];
[UIView animateKeyframesWithDuration:duration
delay:0.0
options:0
animations:^{
[UIView addKeyframeWithRelativeStartTime:0.0
relativeDuration:1.0
animations:^{
flippedSectionOfView.layer.transform = [self rotate:self.isReverse];
if (self.isReverse)
{
flippedSectionOfView.frame = CGRectMake(0, 0, CGRectGetWidth(containerView.frame), CGRectGetHeight(containerView.frame));
} else
{
flippedSectionOfView.frame = CGRectMake(0, CGRectGetHeight(flippedSectionOfView.frame)*2, CGRectGetWidth(flippedSectionOfView.frame), CGRectGetHeight(flippedSectionOfView.frame));
}
}];
} completion:^(BOOL finished) {
if ([transitionContext transitionWasCancelled])
{
[toView removeFromSuperview];
} else {
[fromView removeFromSuperview];
}
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
}
- (CATransform3D)rotate:(BOOL)initTransform
{
CATransform3D transform = initTransform ? CATransform3DMakeRotation(0.0, 0.0, 0.0, 0.0) : CATransform3DMakeRotation(-M_PI_2, 0.0, 0.0, -2.0);
return transform;
}
@end
ここで、遷移前と遷移後のクラスのviewを使用して、どのようにアニメーションさせるかを実装していきます。
今回は遷移時に遷移前のviewが崩れ落ち、前の画面に戻ると遷移元のviewが崩れ落ちるアニメーションの逆再生で戻るように実装しました。
このように自分の好きなアニメーションが作成できますので、自分のアプリをよりユニークに作成することができます。
今回はボタンで画面を遷移しましたが、フリックに合わせてアニメーションしながら画面を遷移させることも可能ですので、また次回に記載していきたいと思います。


