一、引言
关于动画在iOS开发中的应用,曾经整理过一系列的博客进行总结。包括简单的UIView层的动画,CALayer层的动画,Autolayout自动布局动画以及CoreAnimation核心动画框架等。本篇博客主要深入讨论视图控制器、导航控制器来进行界面跳转时的专场动画相关内容。之前的动画相关博客列举如下:
iOS动画开发之一——UIViewAnimation动画的使用:https://my.oschina.net/u/2340880/blog/484457
iOS动画开发之二——UIView动画执行的另一种方式:https://my.oschina.net/u/2340880/blog/484538
iOS动画开发之三——UIView的转场切换:https://my.oschina.net/u/2340880/blog/484669
iOS动画开发之四——核心动画编程(CoreAnimation):https://my.oschina.net/u/2340880/blog/484793
iOS动画开发之五——炫酷的粒子效果:https://my.oschina.net/u/2340880/blog/485095
iOS开发CoreAnimation解读之一——初识CoreAnimation核心动画编程:https://my.oschina.net/u/2340880/blog/535235
iOS开发CoreAnimation解读之二——对CALayer的分析:https://my.oschina.net/u/2340880/blog/536048
iOS开发CoreAnimation解读之三——几种常用Layer的使用解析:https://my.oschina.net/u/2340880/blog/538024
iOS开发CoreAnimation解读之四——Layer层动画内容:https://my.oschina.net/u/2340880/blog/539599
iOS开发CoreAnimation解读之五——高级动画技巧:https://my.oschina.net/u/2340880/blog/539827
iOS开发CoreAnimation解读之五——CATransform3D变换的应用:https://my.oschina.net/u/2340880/blog/539878
iOS中播放gif动态图的方式探讨:https://my.oschina.net/u/2340880/blog/608560
iOS界面布局之三——纯代码的autoLayout及布局动画:https://my.oschina.net/u/2340880/blog/524089
开始本篇博客前,先上一张图,如果你觉得不好理解,没关系,看完后面的内容再回来看这张图,就一目了然了。
二、UIViewController进行模态跳转的转场
首先,使用CoreAnimation框架中的CATransition类也可以实现视图控制器的转场动画,前面的博客有过讨论,这里不再重复。presentViewController这个函数使用率可谓是非常高的,默认的转场动画为新的视图控制器从下向上弹出,dismissViewControllerAnimated函数的返回动画则是弹出动画的逆序播放。其实,系统提供了4种转场动画供开发者选择,通过设置将要弹出的UIViewController实例的如下属性:
@property(nonatomic,assign) UIModalTransitionStyle modalTransitionStyle;
UIModalTransitionStyle是一个枚举,如下:
typedef NS_ENUM(NSInteger, UIModalTransitionStyle) {
UIModalTransitionStyleCoverVertical = 0, //从下向上弹起 默认项
UIModalTransitionStyleFlipHorizontal __TVOS_PROHIBITED, //水平翻转
UIModalTransitionStyleCrossDissolve, //渐隐渐现
UIModalTransitionStylePartialCurl NS_ENUM_AVAILABLE_IOS(3_2) __TVOS_PROHIBITED, //翻页
};
很多时候,上面4种枚举的转场动画样式并不能满足我们的需求,我们可以使用UIViewControllerTransitioningDelegate协议来完全自定义想要的转场动画效果。
首先创建一个类,使其遵守UIViewControllerTransitioningDelegate协议,比如我这里将类名去做TransDelegate,继承自NSObject。在界面跳转时,将要弹出的视图控制器设置如下:
ViewController2 * v2 = [ViewController2 new];
self.transDelegate = [[TransDelegate alloc]init];
v2.transitioningDelegate = self.transDelegate;
[self presentViewController:v2 animated:YES completion:nil];
我们先来看UIViewControllerTransitioningDelegate协议中的如下几个函数:
//这个函数用来设置当执行present方法时 进行的转场动画
/*
presented为要弹出的Controller
presenting为当前的Controller
source为源Contrller 对于present动作 presenting与source是一样的
*/
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source;
//这个函数用来设置当执行dismiss方法时 进行的转场动画
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed;
//这个函数用来设置当执行present方法时 进行可交互的转场动画
- (nullable id <UIViewControllerInteractiveTransitioning>)interactionControllerForPresentation:(id <UIViewControllerAnimatedTransitioning>)animator;
//这个函数用来设置当执行dismiss方法时 进行可交互的转场动画
- (nullable id <UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id <UIViewControllerAnimatedTransitioning>)animator;
//iOS8后提供的新接口 返回UIPresentationController处理转场
- (nullable UIPresentationController *)presentationControllerForPresentedViewController:(UIViewController *)presented presentingViewController:(nullable UIViewController *)presenting sourceViewController:(UIViewController *)source NS_AVAILABLE_IOS(8_0);
我们先来看上面的前两个函数,这两个函数都要返回一个实现了UIViewControllerAnimatedTransitioning协议的对象,UIViewControllerAnimatedTransitioning则用来负责具体的动画展示,例如我们在创建一个命名为AniObject的类,继承自NSObject,使其实现UIViewControllerAnimatedTransitioning协议,在TransDelegate类中实现如下:
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source{
return [AniObject new];
}
下面我们来实现AniObject类来具体的处理动画效果:
UIViewControllerAnimatedTransitioning协议中有两个函数是必须实现的,如下:
//这个函数用来设置动画执行的时长
- (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext{
return 2;
}
//这个函数用来处理具体的动画
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext{
//跳转的界面
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
//最终的位置
CGRect finalRect = [transitionContext finalFrameForViewController:toVC];
//起始位置
toVC.view.frame = CGRectOffset(finalRect, [[UIScreen mainScreen]bounds].size.width, 0);
//添加到内容视图
[[transitionContext containerView]addSubview:toVC.view];
//执行动画
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
toVC.view.frame = finalRect;
} completion:^(BOOL finished) {
//完成动画
[transitionContext completeTransition:YES];
}];
}
上面我们实现了一个简单的自定义转场动画,将present动画修改成了从右侧滑入,但是dismiss动画依然是默认的从下方划出。
下面我们来分析下transitionContext这个对象,这个对象实际上是一个转场上下文,使用它来进行动画的定义和执行:
//容器视图 用来表现动画
@property(nonatomic, readonly) UIView *containerView;
//下面是几个只读属性
//是否应该执行动画
@property(nonatomic, readonly, getter=isAnimated) BOOL animated;
//是否是可交互的
@property(nonatomic, readonly, getter=isInteractive) BOOL interactive; // This indicates whether the transition is currently interactive.
//是否被取消了
@property(nonatomic, readonly) BOOL transitionWasCancelled;
//转场风格
@property(nonatomic, readonly) UIModalPresentationStyle presentationStyle;
//调用这个函数来更新转场过程的百分比 用于可交互动画的阈值
- (void)updateInteractiveTransition:(CGFloat)percentComplete;
//完成可交互的转场交互动作时调用
- (void)finishInteractiveTransition;
//取消可交互的转场交互动作时调用
- (void)cancelInteractiveTransition;
//转场动画被中断 暂停时调用
- (void)pauseInteractiveTransition;
//转场动画完成时调用
- (void)completeTransition:(BOOL)didComplete;
//获取转场中的两个视图控制器
/*
UITransitionContextViewControllerKey的定义
UITransitionContextFromViewControllerKey //原视图控制器
UITransitionContextToViewControllerKey //跳转的视图控制器
*/
- (nullable __kindof UIViewController *)viewControllerForKey:(UITransitionContextViewControllerKey)key;
//直接获取转场中的视图
/*
UITransitionContextFromViewKey //原视图
UITransitionContextToViewKey //转场的视图
*/
- (nullable __kindof UIView *)viewForKey:(UITransitionContextViewKey)key;
//获取视图控制器的初识位置
- (CGRect)initialFrameForViewController:(UIViewController *)vc;
//获取视图控制器转场后的位置
- (CGRect)finalFrameForViewController:(UIViewController *)vc;
通过上面的介绍,我们可以使用UIViewControllerContextTransitioning随心所欲的定制转场动画,但是还有一个困难我们无法克服,那就是可以交互的转场动画。我们在使用系统的导航控制器时,右划返回效果对用户体验十分友好,我们下面就来试着将视图控制器的模态跳转设计成类似导航可交互的。