首先,实现一个非常简单的UINavigationController转场,一般会这么干
实现FirstViewController,加到Window上(没用storyboard和xib)
实现FirstViewController上面有个按钮,点击后push到SecondViewController
贴一下FirstViewController的关键代码,这很简单
- (void)viewDidLoad {
[super viewDidLoad];
//init First
self.navigationItem.title = @"First";
self.view.backgroundColor = [UIColor orangeColor];
//init Second
secondViewController = [[SecondViewController alloc] init];
// Push
UIButton *pushButton = [UIButton buttonWithType:UIButtonTypeSystem];
pushButton.frame = CGRectMake(140, 200, 40, 40);
[pushButton setTitle:@"Push" forState:UIControlStateNormal];
[pushButton addTarget:self action:@selector(push) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:pushButton];
}
- (void)push
{
[self.navigationController pushViewController:secondViewController animated:YES];
}
下面我们要自定义这个push动画
我们先实现一个push自定义动画类姑且叫做CustomPushAnimation,它实现UIViewControllerAnimatedTransitioning协议,用来定义一个非交互动画(就是动画过程中没交互)
里面也用到了UIViewControllerContextTransitioning这个协议,可以理解为转场动画的上下文,一个容器。下面是代码.h和.m,记住这是一个自定义的push的动画
#import
#import
@interface CustomPushAnimation : NSObject
@end
#import "CustomPushAnimation.h"
@implementation CustomPushAnimation
- (NSTimeInterval)transitionDuration:(id)transitionContext
{
return 3.0;
}
- (void)animateTransition:(id)transitionContext
{
//目的ViewController
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
//起始ViewController
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
//添加toView到上下文
[[transitionContext containerView] insertSubview:toViewController.view belowSubview:fromViewController.view];
//自定义动画
toViewController.view.transform = CGAffineTransformMakeTranslation(320, 568);
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
fromViewController.view.transform = CGAffineTransformMakeTranslation(-320, -568);
toViewController.view.transform = CGAffineTransformIdentity;
} completion:^(BOOL finished) {
fromViewController.view.transform = CGAffineTransformIdentity;
// 声明过渡结束时调用 completeTransition: 这个方法
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
}
@end
//FirstViewController viewDidLoad
self.navigationController.delegate = self;
//init CustomPush
customPush = [[CustomPushAnimation alloc] init];
为什么要添加代理呢?怎么用这个CustomPush动画呢?
答案就是我们要用UINavigationControllerDelegate的
– (id<UIViewControllerAnimatedTransitioning>) navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC
这个方法来选中Push转场的时候用我们自己定义的CustomPush动画。
#pragma mark - UINavigationControllerDelegate iOS7非交互自定义Navigation转场
// 动画特效
- (id) navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC
{
/**
* typedef NS_ENUM(NSInteger, UINavigationControllerOperation) {
* UINavigationControllerOperationNone,
* UINavigationControllerOperationPush,
* UINavigationControllerOperationPop,
* };
*/
//push的时候用我们自己定义的customPush
if (operation == UINavigationControllerOperationPush) {
return customPush;
}else{
return nil;
}
}
很有趣吧,下面照葫芦画瓢,做一个Pop动画吧!聪明的你一定知道,只需要在自定义转场的代理里面加一个if语句判断pop就好
// 动画特效
- (id) navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC
{
/**
* typedef NS_ENUM(NSInteger, UINavigationControllerOperation) {
* UINavigationControllerOperationNone,
* UINavigationControllerOperationPush,
* UINavigationControllerOperationPop,
* };
*/
if (operation == UINavigationControllerOperationPush) {
return customPush;
}else if (operation == UINavigationControllerOperationPop) {
return customPop;
}else{
return nil;
}
}
NavigationController搞定,Present也依葫芦画瓢
工程里添加第三个ViewController 叫做ThirdViewController,然后在FirstViewController里面添加下面代码
//init Third
thirdViewController = [[ThirdViewController alloc] init];
// Present
UIButton *presentButton = [UIButton buttonWithType:UIButtonTypeSystem];
presentButton.frame = CGRectMake(110, 400, 100, 40);
[presentButton setTitle:@"Present" forState:UIControlStateNormal];
[presentButton addTarget:self action:@selector(present) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:presentButton];
然后ThirdViewController添加一个按钮用来Dismiss自己,这个就不写了,然后运行起来实现的功能就很正常,点击Present按钮就会自下而上的一个动画推入新的ViewController,这很正常,然后现在来实现高级动画
首先,和Navigation类似,在FirstViewController我们要做一些准备工作
修改FirstViewController的协议
@interface FirstViewController ()
thirdViewController.transitioningDelegate = self;
#pragma mark - UIViewControllerTransitioningDelegate
-(id)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
{
customPresent.animationType = AnimationTypePresent;
return customPresent;
}
-(id)animationControllerForDismissedController:(UIViewController *)dismissed
{
customPresent.animationType = AnimationTypeDismiss;
return customPresent;
}
那么,customPresent.animationType = AnimationTypePresent;这是什么东西呢?不要着急,这个就是自定义的Present动画效果,下面就讲他的实现,但是目前已经可以看到代码写起来和NavigationController的自定义动画非常像,对仗工整,都是协议,设置代理,调用代理时候使用自定义的动画。
下面定义真正的自定义动画CustomPresentAnimation.h
typedef enum {
AnimationTypePresent,
AnimationTypeDismiss
} AnimationType;
#import
#import
@interface CustomPresentAnimation : NSObject
@property (nonatomic, assign) AnimationType animationType;
@end
#import "CustomPresentAnimation.h"
@implementation CustomPresentAnimation
- (NSTimeInterval)transitionDuration:(id)transitionContext
{
return 1.3;
}
- (void)animateTransition:(id )transitionContext {
UIViewController* toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIViewController* fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIView * toView = toViewController.view;
UIView * fromView = fromViewController.view;
if (self.animationType == AnimationTypePresent) {
//snapshot方法是很高效的截屏
//First放下面
UIView * snap = [fromView snapshotViewAfterScreenUpdates:YES];
[transitionContext.containerView addSubview:snap];
//Third放上面
UIView * snap2 = [toView snapshotViewAfterScreenUpdates:YES];
[transitionContext.containerView addSubview:snap2];
snap2.transform = CGAffineTransformMakeTranslation(-320, 0);
//进行动画
[UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0 usingSpringWithDamping:0.5 initialSpringVelocity:0 options:UIViewAnimationOptionCurveLinear animations:^{
snap2.transform = CGAffineTransformIdentity;
} completion:^(BOOL finished) {
//删掉截图
[snap removeFromSuperview];
[snap2 removeFromSuperview];
//添加视图
[[transitionContext containerView] addSubview:toView];
//结束Transition
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
} else {
//First 放下面
UIView * snap = [toView snapshotViewAfterScreenUpdates:YES];
[transitionContext.containerView addSubview:snap];
//Third 放上面
UIView * snap2 = [fromView snapshotViewAfterScreenUpdates:YES];
[transitionContext.containerView addSubview:snap2];
//进行动画
[UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0 usingSpringWithDamping:0.5 initialSpringVelocity:0 options:UIViewAnimationOptionCurveLinear animations:^{
snap2.transform = CGAffineTransformMakeTranslation(-320, 0);
} completion:^(BOOL finished) {
//删掉截图
[snap removeFromSuperview];
[snap2 removeFromSuperview];
//添加视图
[[transitionContext containerView] addSubview:toView];
//结束Transition
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
}
}
@end
调用的时候就和上面代码所说一样,在UIViewControllerTransitioningDelegate代理里完成,那么自定义的Present转场就是这样了。运行一下,会发现很调皮的ThirdView在Present转场的时候从左面弹性的进入屏幕。
想想iOS7的UINavigationController自带的动画,可以用手指慢慢往左滑动来pop出一个页面,动画是随着手指的移动交互式呈现的,这个就很有趣了,那么除了系统帮我们实现的,我们自己也可以自定义交互式动画了。
下面就来给我们的App添加一个Pan手势,给之前实现的Present动画增加一个交互式功能。
FirstViewController的viewdidload添加pan手势
UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(didClickPanGestureRecognizer:)];
[self.navigationController.view addGestureRecognizer:panRecognizer];
#pragma mark - 手势交互的主要实现--->UIPercentDrivenInteractiveTransition
- (void)didClickPanGestureRecognizer:(UIPanGestureRecognizer*)recognizer
{
UIView* view = self.view;
if (recognizer.state == UIGestureRecognizerStateBegan) {
interactionController = [[UIPercentDrivenInteractiveTransition alloc] init];
[self presentViewController:thirdViewController animated:YES completion:nil];
} else if (recognizer.state == UIGestureRecognizerStateChanged) {
CGPoint translation = [recognizer translationInView:view];
CGFloat distance = fabs(translation.x / CGRectGetWidth(view.bounds));
[interactionController updateInteractiveTransition:distance];
} else if (recognizer.state == UIGestureRecognizerStateEnded) {
CGPoint translation = [recognizer translationInView:view];
CGFloat distance = fabs(translation.x / CGRectGetWidth(view.bounds));
if (distance > 0.5) {
[interactionController finishInteractiveTransition];
} else {
[interactionController cancelInteractiveTransition];
}
interactionController = nil;
}
}
这里需要提一下,初始化下面的代码,用于交互式转场,
//通过 UIViewControllerInteractiveTransitioning 协议进行交互转场。
UIPercentDrivenInteractiveTransition* interactionController;
然后根据UIViewControllerTransitioningDelegate这个协议添加两个方法,来说明我们使用交互式转场
- (id )interactionControllerForPresentation:(id )animator
{
return interactionController;
}
- (id )interactionControllerForDismissal:(id )animator
{
return nil;
}