简单自定义ViewController跳转动画

在学习iOS动画时看到,别人炫酷的动画效果一直是我学习的动力,在学习如何自定义后发现其实并没那么难,一些渐变,翻转,平移等在UIView上能实现的动画都可以放到跳转动画里,做完例子你也可以,分享一个github上别人做的炫酷的动画效果,:
[图片上传失败...(image-a93858-1517451898113)]](http://upload-images.jianshu.io/upload_images/1640181-8f6e494722bd2429.gif?imageMogr2/auto-orient/strip)

原理

当我们进行视图切换的时候,无论是push,pop还是通过PresentModal和dismiss时,我们可以把跳转动画分割为三个部分,
1:第一个视图控制器显示。
2:临时视图控制器 显示动画效果(并不是真的存在这个控制器,其实只有一个containerView)
3:显示第二个视图控制器。
用形象一点的说法,我们可以将第二部分也看做是一个临时的视图控制器,他执行一段关于UIView的动画,我们只要让动画的开始界面与控制器1一样,结束界面与视图控制器2一样。当动画执行结束,执行到第三步时,把临时的视图控制器释放。这样只要稍微实现一些UIView,CALayer的动画,
具体的实现方法以下图的效果为例子:


跳转动画Demo.gif

用到的协议及方法

@protocol UIViewControllerAnimatedTransitioning

实现这个协议的对象就相当于是之前说到的临时视图控制器,协议中主要用到的两个方法是:

//这个方法中我们只要返回页面跳转动画的执行时间
-(NSTimeInterval)transitionDuration:(id < UIViewControllerContextTransitioning >)transitionContext;
//在这个方法中实现具体动画
-(void)animateTransition:(id < UIViewControllerContextTransitioning >)transitionContext;

第二个方法是跳转动画核心内容,方法中的参数transitionContext是当前临时控制器界面的上下文。通过上下文,我们先拿到临时视图控制器的背景视图containerView,他已经有一个子视图,即fromViewController.view。我们要做的是,先获得toViewController

UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];

然后把当前视图加到内容视图上

[[transitionContext containerView] addSubview:toVC.view];

剩下的部分就可以发挥我们的系统核心动画的学习水平,比如修改toVC.view的frame,从下方旋转弹出等等。而要实现Demo里的效果,我们需要CAShapeLayer来给toView做遮挡,然后从修改ShapeLayer.path属性,从一个小圆变成一个囊括全屏的大圆,小圆大圆我们通过UIBezierPath画出来,对于ShapeLayer和UIBezierPath不熟悉的同学可以先去补习一下,这里具体的使用也不复杂,也可以直接看下面的代码学习。

//应用程序的屏幕高度
#define kWindowH   [UIScreen mainScreen].bounds.size.height
//应用程序的屏幕宽度
#define kWindowW    [UIScreen mainScreen].bounds.size.width

_circleCenterRect = CGRectMake(120, 320, 50, 50);
//圆圈1--小圆
UIBezierPath *smallCircleBP =  [UIBezierPath bezierPathWithOvalInRect:_circleCenterRect];
//圆圈2--大圆
//以_circleCenterRect的中心为圆心
CGFloat centerX = _circleCenterRect.origin.x+_circleCenterRect.size.width/2;
CGFloat centerY = _circleCenterRect.origin.y+_circleCenterRect.size.height/2;
//找出圆心到页面4个角 最长的距离 作为半径
CGFloat r1 = (kWindowW-centerX)>centerX?(kWindowW-centerX):centerX;
CGFloat r2 = (kWindowW-centerY)>centerY?(kWindowW-centerY):centerY;
CGFloat radius = sqrt((r1 * r1) + (r2 * r2));
UIBezierPath *bigCircleBP = [UIBezierPath bezierPathWithOvalInRect:CGRectInset(_circleCenterRect, -radius, -radius)];

画完圆后通过CABasicAnimation执行:

//设置maskLayer
CAShapeLayer *maskLayer = [CAShapeLayer layer];//将它的 path 指定为最终的 path 来避免在动画完成后会回弹
toVC.view.layer.mask = maskLayer;
maskLayer.path = bigCircleBP.CGPath;
//执行动画
CABasicAnimation *maskLayerAnimation = [CABasicAnimation animationWithKeyPath:@"path"];
maskLayerAnimation.fromValue = (__bridge id)(smallCircleBP.CGPath);
maskLayerAnimation.toValue = (__bridge id)((bigCircleBP.CGPath));
maskLayerAnimation.duration = [self transitionDuration:transitionContext];
maskLayerAnimation.delegate = self;
[maskLayer addAnimation:maskLayerAnimation forKey:@"path"];

返回时候的动画效果和上面的代码基本相同,只用将大圆和小圆的顺序对调,完成了跳转动画最核心的部分后就是怎么使用这个对象了,无论是navigation和模态跳转,系统都给我们提供了回调方法,我们只需要实现方法,然后把我们之前写好的动画对象返回就可以了。由于给视图添加了shapelayer,可以在CABasicAnimation的代理方法里实现。

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
    //告诉 系统这个 transition 完成
    [self.transitionContext completeTransition:![self.transitionContext transitionWasCancelled]];
    //清除 fromVC 的 mask
    [self.transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey].view.layer.mask = nil;
    [self.transitionContext viewControllerForKey:UITransitionContextToViewControllerKey].view.layer.mask = nil;
}
@protocol UINavigationControllerDelegate

这是修改Navigation跳转动画需要实现的协议,当我们通过pop或者push进行跳转时,都会调用下面的方法(协议的其他方法由于暂时用不到,所以就不介绍了),我们可以看到返回值是id <UIViewControllerAnimatedTransitioning>,就是我们之前编写的动画对象。
- (nullable id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
animationControllerForOperation:(UINavigationControllerOperation)operation
fromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC;
方法中的operation参数是一个枚举类型,我们可以判断它的值是 UINavigationControllerOperationPush还是
UINavigationControllerOperationPop来区别当前的跳转方式。

@protocol UIViewControllerTransitioningDelegate

这是修改模态跳转动画需要实现的协议,我们进行跳转PresentModal和dismiss时会分别调用两个方法:

//PresentModal跳转回调方法
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source;
//dismiss跳转回调方法
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed;

这两个方法的返回值和上面navigation回调方法的返回值一样,我们只需要初始化我们的实现了UIViewControllerTransitioningDelegate协议的动画对象就可以了。具体的实现以及效果可以查看文章末尾附上的Demo。

navigation跳转需要注意的地方

在使用Navigation跳转的时候,我们希望视图1跳转到视图2和视图2返回视图1有自定义动画时,我们只用在视图1中将设置视图1的navigation的delagate。其次delegate的设置最好放在viewWillAppear中。

demo所实现的最终效果只出现在点击按钮时的跳转动画,而不包括像navigation侧滑返回那样的手势动画。如何怕侧滑效果不统一,我们可以先把侧滑返回的效果关闭:

self.navigationController.interactivePopGestureRecognizer.enabled = NO; 

// TODO:手势跳转动画实现方法(坑 待填)

github Demo地址
bestswifter 的swift版教程

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,066评论 4 62
  • 内容抽屉菜单ListViewWebViewSwitchButton按钮点赞按钮进度条TabLayout图标下拉刷新...
    皇小弟阅读 46,732评论 22 665
  • 随着经济危机的来临,工作机会变得珍贵起来。大家都知道不好找工作,所以对公司的加班,不涨薪 等等都默默忍受。也许...
    对面不相识阅读 284评论 1 0
  • 不会忘记,你这样的女子。有扬在脸上的自信,长在心底的善良,融进血里的骨气,清风拂面的温柔,刻进命里的坚强。但,你欠...
    释若读书阅读 1,177评论 9 7
  • zimbra 邮件服务器部署 系统环境:CentOS Linux release 7.3.1611 (Core) ...
    JuFaLu阅读 1,129评论 0 0