https://www.jianshu.com/p/39c051cfe7dd

CATransition

CATransition 是CAAnimation的子类(如下图所示),用于控制器和控制器之间的转场动画。能够来自定义系统的push和present。实现页面间的动画效果。

转场动画就是从一个场景以动画的形式过渡到另一个场景。转场动画的使用一般分为以下几个步骤:

  • 创建转场动画
  • 设置转场类型、子类型(可选)及其他属性画
  • 设置转场后的新视图并添加动画到图层

ios的画面切换的动画效果的API主要通过调用系统已定义的动画效果实现,这些效果已基本囊括开发的需求,如果需要更加复杂的效果,可以考虑CATransition来实现。

以下是基本的四种效果

kCATransitionPush 推入效果
kCATransitionMoveIn 移入效果
kCATransitionReveal 截开效果
kCATransitionFade 渐入渐出效果

以下API效果可以安全使用
cube 方块
suckEffect 三角
rippleEffect 水波抖动
pageCurl 上翻页
pageUnCurl 下翻页
oglFlip 上下翻转
cameraIrisHollowOpen 镜头快门开
cameraIrisHollowClose 镜头快门开

以下API效果请慎用

spewEffect 新版面在屏幕下方中间位置被释放出来覆盖旧版面.
genieEffect 旧版面在屏幕左下方或右下方被吸走, 显示出下面的新版面
unGenieEffect 新版面在屏幕左下方或右下方被释放出来覆盖旧版面.
twist 版面以水平方向像龙卷风式转出来.
tubey 版面垂直附有弹性的转出来.
swirl 旧版面360度旋转并淡出, 显示出新版面.
charminUltra 旧版面淡出并显示新版面.
zoomyIn 新版面由小放大走到前面, 旧版面放大由前面消失.
zoomyOut 新版面屏幕外面缩放出现, 旧版面缩小消失.
oglApplicationSuspend 像按”home” 按钮的效果.

// 使用场景 1

1.初始化

CATransition  *transition = [CATransition animation];

2.设置动画时长,设置代理人

transition.duration = 1.0f;

transition.delegate = self;

3.设置切换速度效果

transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];

枚举值:

kCAMediaTimingFunctionLinear

kCAMediaTimingFunctionEaseIn

kCAMediaTimingFunctionEaseOut

kCAMediaTimingFunctionEaseInEaseOut

kCAMediaTimingFunctionDefault

4.动画切换风格

transition.type = kCATransitionFade;

枚举值:

kCATransitionFade = 1,     // 淡入淡出

kCATransitionPush,         // 推进效果

kCATransitionReveal,       // 揭开效果

kCATransitionMoveIn,       // 慢慢进入并覆盖效果

5.动画切换方向

transition.subtype = kCATransitionFromTop;//顶部

枚举值:

kCATransitionFromRight//右侧

kCATransitionFromLeft//左侧

kCATransitionFromTop//顶部

kCATransitionFromBottom//底部

6.进行跳转

[self.navigationController.view.layer addAnimation:transition forKey:nil];

[self.navigationController pushViewController:"你要跳转的页面" animated:NO];

跳转动画一定设置为NO,不然会两个效果叠加

// 使用场景2

 

+ (void)transitionWithView:(UIView *)view duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options animations:(void (^ __nullable)(void))animations completion:(void (^ __nullable)(BOOL finished))completion NS_AVAILABLE_IOS(4_0);

+ (void)transitionFromView:(UIView *)fromView toView:(UIView *)toView duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options completion:(void (^ __nullable)(BOOL finished))completion NS_AVAILABLE_IOS(4_0); // toView added to fromView.superview, fromView removed from its superview

CATransition-Basic -代码传送门

下面通过自定义push 和 present 两个demo来分析下转场动画的实现原理:

自定义 Push

首先要完场自定义Push 我们需要创建一个转场动画对象,继承于NSObject (导入<UIKit/UIKit.h>框架)并遵守UIViewControllerAnimatedTransitioning协议, 这里叫做MagicMoveTransition。

#import <Foundation/Foundation.h>

#import <UIKit/UIKit.h>

@interface MagicMoveTransition : NSObject<UIViewControllerAnimatedTransitioning>

@end

我们需要实现UIViewControllerAnimatedTransitioning协议里面的代理方法来实现转场动画效果。

该方法返回动画执行时间

、、、、、

该方法用来实现两个VC间的动画布局

、、、、、

transitionContext 该参数 可以让我们去访问一些实现对象所必须的对象

 

扩展 UIViewControllerContextTransitioning

1.  - (UIView *)containerView;

// 转场动画的容器 -> 添加两个控制器 视图内容 (注意添加的前后顺序)

2.  - (UIViewController *)viewControllerForKey:(NSString *)key;

// 通过该方法, Key值 拿到过渡的两个VC

3.  - (CGRect)initialFrameForViewController:(UIViewController *)vc;

- (CGRect)finalFrameForViewController:(UIViewController *)vc;

// 通过这个方法 能够获得前后两个 ViewController 的frame,用来布局视图对象空间位置

现在我们可以通过 transitionContext开始做转场动画

// 起始VC

ViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];

// 目的VC

DeatailViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];

// 转场视图容器

UIView *containerView = [transitionContext containerView];

然后我们需要获取CollectionViewCell 上面的视图ImageView 进行做动画

扩展  iOS  , UIView 中坐标的转换

- (CGPoint)convertPoint:(CGPoint)point toView:(UIView *)view;

// 将像素Point 由Point所在控制器转换到目标控制器视图View中, 返回在目标视图中的像素值。

- (CGPoint)convertPoint:(CGPoint)point fromView:(UIView *)view;

// 由上面一条相反,获取目标View的像素Point返回到当前控制器View中

- (CGRect)convertRect:(CGRect)rect toView:(UIView *)view;

// 将坐标frame的rect值,由当前所在的目标视图View中, 返回在当前视图中的rect

- (CGRect)convertRect:(CGRect)rect fromView:(UIView *)view;

// 将坐标frame的rect值,由当前所在的目标视图View中, 返回在当前视图中的rect

我们在这里通过获取我们点击的Cell,获取上面的ImageView

- (nullable NSArray<NSIndexPath *> *)indexPathsForSelectedItems; // returns nil or an array of selected index paths

- (nullable UICollectionViewCell *)cellForItemAtIndexPath:(NSIndexPath *)indexPath;

具体代码如下

NSIndexPath *indexPath = [[fromVC.collectionView indexPathsForSelectedItems] firstObject];

fromVC.indexPath = indexPath; // 记录indexPath 返回时使用

CustomCollectionViewCell *cell = (CustomCollectionViewCell *)[fromVC.collectionView cellForItemAtIndexPath:indexPath];

// 对Cell上面的图片 做截图 来实现过渡动画视图

UIView *screenShot = [cell.pic snapshotViewAfterScreenUpdates:NO];

screenShot.backgroundColor = [UIColor clearColor];

screenShot.frame = fromVC.finiRect = [containerView convertRect:cell.pic.frame fromView:cell.pic.superview];

// fromVC.finiRect 返回时获取cell上的坐标

cell.pic.hidden = YES;

// 在进入下一个界面时,隐藏当前Cell上面的imageView

然后我们 来 设置第二个控制器的位置和透明度

toVC.view.frame = [transitionContext finalFrameForViewController:toVC];

toVC.view.alpha = 0.5;

toVC.secondPic.hidden = YES;

// 把动画前后的两个ViewController加到容器控制器中

[containerView addSubview:toVC.view];

[containerView addSubview:screenShot];

// 注意添加顺序

现在开始做动画,可以根据你的项目需求来获取,控制器内的控件,来完成彼此间的动画交互。

这里 :[self transitionDuration:transitionContext]会返回转场动画所进行时间。

执行该语句来告诉系统转场动画结束, 这里使用!transitionContext.transitionWasCancelled,是为了后面做手势交互,避免手势取消时,造成卡顿现象。

[transitionContext completeTransition:!transitionContext.transitionWasCancelled];

}];

[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{

// 调用自动布局

[containerView layoutIfNeeded];

fromVC.view.alpha = 1.0;

// 布局坐标

// 获取到下一个VC中的secondPic.frame,使Cell上面的截图,移动到第二VC视图中, 动画结束后, 移除该截图。视觉上会产生一种过渡效果。

screenShot.frame = [containerView convertRect:toVC.secondPic.frame toView:toVC.secondPic.superview];

} completion:^(BOOL finished) {

toVC.secondPic.hidden = NO;

cell.pic.hidden = NO;

// 动画截图移除View

[screenShot removeFromSuperview];

toVC.view.alpha = 1;

// 动画结束

// 一定不要忘记告诉系统动画结束

// 执行

[transitionContext completeTransition:!transitionContext.transitionWasCancelled];

}];

现在我们就可以来实现来使用自定义的转场动画对象

这里的转场动画属于自定义push, 由导航栏来控制,属于导航控制器来负责转场。我们让当前控制器来作为 UINavigationControllerDelegate代理对象, 并实现协议方法。

在视图已经出现的时候来指定代理对象为自身

- (void)viewDidAppear:(BOOL)animated

{

[super viewDidAppear:animated];

self.navigationController.delegate = self;

}

#pragma mark - 返回我们写好的转场对象

- (nullable id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController

animationControllerForOperation:(UINavigationControllerOperation)operation

fromViewController:(UIViewController *)fromVC

toViewController:(UIViewController *)toVC

{

if ([toVC isKindOfClass:[DeatailViewController class]]) {

MagicMoveTransition *transition = [[MagicMoveTransition alloc] init];

return transition;

}

return nil;

}

到这里我们push转场动画就已经实现了, pop回来的动画我们可以类比push来实现。

#import "MagicMoveBackTransition.h"

#import "ViewController.h"

#import "DeatailViewController.h"

#import "CustomCollectionViewCell.h"

@implementation MagicMoveBackTransition

#pragma mark - 返回动画时间

- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext

{

return 0.6f;

}

#pragma mark - 两个VC过渡动画

- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext

{

// 目的VC

ViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];

// 起始VC

DeatailViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];

// 转场视图容器

UIView *containerView = [transitionContext containerView];

UIView *screenShot = [fromVC.secondPic snapshotViewAfterScreenUpdates:NO];

screenShot.backgroundColor = [UIColor clearColor];

screenShot.frame = [containerView convertRect:fromVC.secondPic.frame fromView:fromVC.secondPic.superview];

fromVC.secondPic.hidden = YES;

// 初始化第二个Vc

toVC.view.frame = [transitionContext finalFrameForViewController:toVC];

CustomCollectionViewCell *cell = (CustomCollectionViewCell *)[toVC.collectionView cellForItemAtIndexPath:toVC.indexPath];

cell.pic.hidden = YES;

[containerView insertSubview:toVC.view belowSubview:fromVC.view];

[containerView addSubview:screenShot];

// 发生动画

[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{

fromVC.view.alpha = 0;

screenShot.frame = toVC.finiRect;

} completion:^(BOOL finished) {

[screenShot removeFromSuperview];

fromVC.secondPic.hidden = NO;

cell.pic.hidden = NO;

fromVC.view.alpha = 1;

// 结束

[transitionContext completeTransition:!transitionContext.transitionWasCancelled];

}];

}

@end

另外我们可以通过添加手势,来能实现系统手势驱动;UIPercentDrivenInteractiveTransition 能够帮助我们完成视图切换的过度动画

在 DeatailViewController pop回来时添加屏幕边缘手势

// 添加屏幕边缘手势

UIScreenEdgePanGestureRecognizer *edgePagGesture = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self action:@selector(edgeAction:)];

edgePagGesture.edges = UIRectEdgeLeft; // 设置什么便捷滑入

[self.view addGestureRecognizer:edgePagGesture];

// 实现手势方法

iOS7 新加入一个类对象 UIPercentDrivenInteractiveTransition ;这个对象会根据我们的手势,来决定我们界面跳转的自定义过渡效果,我们在手势action方法中,对手势驱动状态进行判断,来决定是否过渡动画。

- (void)edgeAction:(UIScreenEdgePanGestureRecognizer *)sender

{

// 计算手指滑动的距离

// 计算手势驱动占屏幕的百分比

CGFloat distance = [sender translationInView:sender.view].x / self.view.bounds.size.width;

distance = MIN(1.0, MAX(0.0, distance));

// 限制百分比 0 - 1 之间

if (sender.state == UIGestureRecognizerStateBegan) {

self.percentDrivenTransition = [[UIPercentDrivenInteractiveTransition alloc] init];

[self.navigationController popViewControllerAnimated:YES];

}

else if (sender.state == UIGestureRecognizerStateChanged)

{

// 手势在慢慢划入 // 把手势的进度告诉 UIPercentDrivenInteractiveTransition

[self.percentDrivenTransition updateInteractiveTransition:distance];

}

else if (sender.state == UIGestureRecognizerStateEnded || sender.state == UIGestureRecognizerStateCancelled)

{

// 判断当手势到达一定范围内

if (distance > 0.5) {

[self.percentDrivenTransition finishInteractiveTransition];

}

else

{

[self.percentDrivenTransition cancelInteractiveTransition];

}

self.percentDrivenTransition = nil;

}

}

// 执行手势驱动代理方法

- (id<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransitioning>)animationController

{

if ([animationController isKindOfClass:[MagicMoveBackTransition class]]) {

return self.percentDrivenTransition;

}

else

{

return nil;

}

}

到这里我们就完成了自定义push

代码传送门

自定义 Present

 
  • 自定义present和自定push相似,自定义present负责转场的是导航控制器,自定present负责的转场的是视图控制器本身.
  • 视图处理的逻辑一样 ;分别实现基于 UIViewControllerAnimatedTransitioning的present 和dismiss 详细参考自定义push,下面讲下区别的地方.

// 在第一个present 推出的视图那里,我们需要让该控制器遵守 <UIViewControllerTransitioningDelegate>

- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
self.transitioningDelegate = self;
}

需要注意的是目标VC也需要遵守该协议;这里以prepareForSegue为例

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
SecondViewController *secondVc = (SecondViewController *)segue.destinationViewController; // 注意一定要把目标值 也作为当前 UIViewControllerTransitioningDelegate的代理人
secondVc.transitioningDelegate = self;
[super prepareForSegue:segue sender:sender];
}

同时目标VC 遵守UIViewControllerTransitioningDelegate

- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
self.transitioningDelegate = self; // 添加屏幕边缘手势
UIScreenEdgePanGestureRecognizer *edgePagGesture = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self action:@selector(edgeAction:)];
edgePagGesture.edges = UIRectEdgeLeft; // 设置什么便捷滑入
[self.view addGestureRecognizer:edgePagGesture]; }

详细的代码这里就不贴出来了.
详情代码
github

作者:Civel_Xu
链接:https://www.jianshu.com/p/39c051cfe7dd
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

iOS CATransition 自定义转场动画的更多相关文章

  1. iOS开发自定义转场动画

    1.转场动画 iOS7之后开发者可以自定义界面切换的转场动画,就是在模态弹出(present.dismiss),Navigation的(push.pop),TabBar的系统切换效果之外自定义切换动画 ...

  2. CATransition自定义转场动画

    我们可以通过CATransiton来自定义一些漂亮的转场动画, CATransition继承自CAAnimation, 所以用法跟CAAnimation差不多 先直接上一个代码: #import &q ...

  3. iOS开发——自定义转场动画

    首先是UIPresentationController,这个控制器给modal新的viewController提供了下一步的view和转场的管理,从一个viewController被modal出来到被 ...

  4. iOS自定义转场动画的实现

    iOS中熟悉的是导航栏中的push和pop这两种动画效果,在这里我们可以自己实现自己想要的一些转场动画 下面是我自己创建转场动画的过程 1.新建一个文件继承自NSObject ,遵循协议UIViewC ...

  5. 一行代码实现自定义转场动画--iOS自定义转场动画集

    WXSTransition 这款非常不错,力推 这是作者源码简书地址: http://www.jianshu.com/p/fd3154946919 这是作者源码github地址 https://git ...

  6. iOS 自定义转场动画

    代码地址如下:http://www.demodashi.com/demo/12955.html 一.总效果 本文记录分享下自定义转场动画的实现方法,具体到动画效果:新浪微博图集浏览转场效果.手势过渡动 ...

  7. iOS 自定义转场动画浅谈

    代码地址如下:http://www.demodashi.com/demo/11612.html 路漫漫其修远兮,吾将上下而求索 前记 想研究自定义转场动画很久了,时间就像海绵,挤一挤还是有的,花了差不 ...

  8. iOS自定义转场动画实战讲解

    iOS自定义转场动画实战讲解   转场动画这事,说简单也简单,可以通过presentViewController:animated:completion:和dismissViewControllerA ...

  9. Swift开发小技巧--自定义转场动画

    自定义转场动画 个人理解为重写了被弹出控制器的modal样式,根据自己的样式来显示modal出来的控制器 例:presentViewController(aVC, animated: true, co ...

随机推荐

  1. python2到python3代码转化:2to3

    Mac系统上: 安装完python3后,2to3可以作为指令直接执行,-w代表"Write back modified files","."代表当前整个文件夹, ...

  2. 接口自动化:HttpClient + TestNG + Java(三) - 初步封装和testng断言

    在上一篇中,我们写了第一个get请求的测试类,这一篇我们来对他进行初步优化和封装 3.1 分离请求发送类 首先想到的问题是,以后我们的接口自动化测试框架会大量用到发送http请求的功能. 那么这一部分 ...

  3. .NET Core中的一个接口多种实现的依赖注入与动态选择看这篇就够了

    最近有个需求就是一个抽象仓储层接口方法需要SqlServer以及Oracle两种实现方式,为了灵活我在依赖注入的时候把这两种实现都给注入进了依赖注入容器中,但是在服务调用的时候总是获取到最后注入的那个 ...

  4. 实例分析C程序运行时的内存结构

      先验知识 静态变量存储在静态存储区,局部变量存储在动态存储区(栈),代码存放在代码区 寄存器,EBP指向栈底,ESP指向栈顶,EIP指向正在执行指令的下一条指令,三个寄存器中保存的都是地址,32位 ...

  5. java内存溢出的情况解决方法

    内存溢出虽然很棘手,但也有相应的解决办法,可以按照从易到难,一步步的解决. 第一步,就是修改JVM启动参数,直接增加内存.这一点看上去似乎很简单,但很容易被忽略.JVM默认可以使用的内存为64M,To ...

  6. OpenCC的编译与多语言使用

    OpenCC全称Open Chinese Convert,是一个Github上面的开源项目,主要用于简繁体汉字的转换,支持语义级别的翻译.本文就来简单介绍一下该库的编译以及python.C++和JAV ...

  7. 【经典案例】Python详解设计模式:策略模式

    完成一项任务往往有多种方式,我们将其称之为策略. 比如,超市做活动,如果你的购物积分满1000,就可以按兑换现金抵用券10元,如果购买同一商品满10件,就可以打9折,如果如果购买的金额超过500,就可 ...

  8. .net core jwt 入门记录

    从百度里搜索里搜索了很多jwt的文章,跟着文章写了一个demo,这里记录下学习过程中碰上的问题.看文章多遍,不如手工实现一次. 模板已上传到github.com:dogvane/webapi_jwt_ ...

  9. Java开发笔记(序)章节目录

    现将本博客的Java学习文章整理成以下笔记目录,方便查阅. 第一章 初识JavaJava开发笔记(一)第一个Java程序Java开发笔记(二)Java工程的帝国区划Java开发笔记(三)Java帝国的 ...

  10. 并发concurrent---3

    背景:并发知识是一个程序员段位升级的体现,同样也是进入BAT的必经之路,有必要把并发知识重新梳理一遍. ConcurrentHashMap:在有了并发的基础知识以后,再来研究concurrent包.普 ...