第三十九篇:IOS核心高级动画 Core Animation、Core Graphics

一、 CALayer < CAMediaTiming > 图层:

1. 图层属性及功能介绍:

1)contents :id 类型, 内容显示。
    在ARC环境下使用: = (__bridge id)image.CGImage;
    在MRC环境下使用:去掉(__bridge id)


2)contentsGravity:字符串型,图层的内容模式。功能与UIView 的contentMode用法相同;比如内容居中显示使用 kCAGravityCenter (值为字符串)。


3)contentsScale:浮点型,内容缩放。每个点与像素个数比例为 1:1。


4)contentsRect:NSRect型,在图层边框只显示寄存图的一个子域,犹如截图 。CGRect类型:不是按点来计算,是按比例计算。
 

5)contentsCenter:CGRect类型,定义一个固定的边框和一个可拉伸的区域。值是0~1(按比例)


6)poistion:CGPoint 类型,相对于父图层,自身的中心点。


7)anchorPoint:CGPoint 类型,锚点。
     锚点即指相对于自身而言的一个点,其范围在0~1之间 或 < 0 或 >1,表示x和y方向分别相对于自身的宽高比例,总与position点重合,而position是相对于父控制位置的点。
     当锚点被设置成相对于自身bounds的点后,就会平移对应的图层,使锚点与中心点重合,就算移动了但中心点是不会变的。


8)zPosition:CGFloat 类型,3D z 轴方向的距离。 可以明显改变屏幕上图层的显示顺序,但不能改变事件的传递顺序。


9)geometryFlipped:view.layer 的 geometryFlipped 属性 对subView的布局产生影响。设置YES,则subView.top 相对父view.bottom 了,也就形成了按水平中心线做了翻转。


CAMediaTiming 动画 协议 会在后面讲到


2. 图层方法有功能介绍:

1)- (void)renderInContext:(CGContextRef)ctx;   截图 功能。如下:
    // 1.截图
    UIGraphicsBeginImageContextWithOptions(self.view.frame.size, YES, 0.0);
    [self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
    // 截取的图片
    UIImage * currentImage = UIGraphicsGetImageFromCurrentImageContext();
    // 在使用UIGraphicsBeginImageContextWithOptions函数一定要有对应的
    // UIGraphicsEndImageContext函数作为结尾,不然会有内存泄漏
    UIGraphicsEndImageContext() ;
  例如下代码:


#import "ViewController.h"

@interface ViewController ()

@property (nonatomic , strong) UIButton * transitionBtn ;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    self.view.backgroundColor = [UIColor redColor];
    self.transitionBtn = [UIButton buttonWithType:UIButtonTypeCustom];
    self.transitionBtn.backgroundColor = [UIColor whiteColor];
    [self.transitionBtn setTitle:@"perform transition" forState:UIControlStateNormal];
    [self.transitionBtn addTarget:self action:@selector(performTransition) forControlEvents:UIControlEventTouchUpInside];
    self.transitionBtn.bounds = CGRectMake(0, 0, 100, 50);
    self.transitionBtn.center = self.view.center ;
    [self.view addSubview:self.transitionBtn];
    
    
}

-(void)performTransition
{
    // 1.截图
    UIGraphicsBeginImageContextWithOptions(self.view.frame.size, YES, 0.0);
    [self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
    // 截取的图片
    UIImage * currentImage = UIGraphicsGetImageFromCurrentImageContext();
    // 在使用UIGraphicsBeginImageContextWithOptions函数一定要有对应的
    // UIGraphicsEndImageContext函数作为结尾,不然会有内存泄漏
    UIGraphicsEndImageContext() ;
    
    
    // 2.把截取的图添加到view上
    UIImageView * currentImageView = [[UIImageView alloc] initWithImage:currentImage];
    currentImageView.frame = self.view.bounds ;
    [self.view addSubview:currentImageView];
    
    // 3.随机改变view的背景色
    CGFloat red = arc4random() / (CGFloat)INT_MAX ;
    CGFloat greed = arc4random() / (CGFloat)INT_MAX ;
    CGFloat blue = arc4random() / (CGFloat)INT_MAX ;
    self.view.backgroundColor = [UIColor colorWithRed:red green:greed blue:blue alpha:1.0];
    
    // 4.设置动画
    [UIView animateWithDuration:1.5 animations:^{
        
        CGAffineTransform transform = CGAffineTransformMakeScale(0.0001f, 0.0001f);
        transform = CGAffineTransformRotate(transform, M_PI_2) ;
        currentImageView.transform = transform ;
        currentImageView.alpha = 0.0 ;
        
    } completion:^(BOOL finished) {
        [currentImageView removeFromSuperview];
    }];

}


2)-(BOOL)containsPoint:(CGPoint)point; 
    功能:当在屏幕点击了图层的某个点point,如果要判断具体点击哪个图层,那么就需要用该方法断每个图层是否包含point点。


3)-(CALayer*)hitTest:(CGPoint)point; 
    功能:直接返回一个currentLayer 的 bounds 所对应的point点所在的最上层的layer对象,即返回真实被点击的layer图层。


4)- (CGPoint)convertPoint:(CGPoint)p fromLayer:(nullable CALayer *)L;
    功能:把 L 图层的bounds 所对应的 point 转成 当前 currentLayer 图层的 bounds 所对应的 point_1 位置;


5)- (CGPoint)convertPoint:(CGPoint)p toLayer:(nullable CALayer *)l;
    功能:把当前currentLayer图层所对应的point转成L图层的bounds所对应的pint_1位置。


6)-(void)layoutSublayerOfLayer:(CALayer *)layer;
     功能:当图层的bounds发生改变或图层的-setNeedsLayout方法被调用时,这个涵数将会被执行。但是不能像UIView的自动布局autorssizingMask 和 constraints属性做到自适应屏幕旋转。这也是为什么最好使用UIView而不单独用图层来构建应用程序的重要原因之一。


3.  边框阴影介绍。

     简介:图层的阴影继承自内容的外形(也就是layer.contents 或subLayer) ,而不是根据边界和角的半径来确定。

1)系统是根据层图的内容来设置阴影,而不是根据边框来设置阴影。
@ 属性设置(低性能)
    1.1 : shadowOpacity :透明度 。
    1.2 : shadowOffset :偏移量:水平和垂直。
    1.3 :shadowRadius :阴影的半径,越大显示越自然。
    1.4 :shadowColor :阴影颜色 。
    1.5 :shadowPath :设定一个巨型边框阴影。


2)例如:
   2.1 如果layer.backgroundColor 是clearColor且layer有一张图片,那么阴影的设置是沿那张图片的边框来设置。
   2.2 如果layer.backgroundColor 不是clearColor且layer有一张图片,那么阴影的设置是沿layer图层的边框来设置


3)性能高方法设置:设置图层的 shadowPaht 属性,这是给定了阴影显示的边框,不用系统去计算,直接显示某形状就OK了。
例如:
self.layer = [CALayer layer];
self.layer.frame = CGRectMake(100, 100, 100, 100);
[self.view.layer addSublayer:self.layer];
self.layer.shadowOpacity = 0.99 ;
// 边框的阴
self.layer.shadowRadius = 20 ;
// 通过 layer.shadowPath 属性来设置阴影范围,性能高
CGMutablePathRef pathRef = CGPathCreateMutable();
 // 矩形边阴影
CGPathAddRect(pathRef, NULL, self.layer.bounds);
self.layer.shadowPath = pathRef ;
CGPathRelease(pathRef);



4. 图层蒙板介绍。

        简介:设置图层的蒙板即 设置 图层的 mask 属性,类型是CALayer,类似一个子图层。

        解释:图层蒙板的Color属性无关紧要,真正重要的是图层的轮廓。mask 就像一个饼干切割机, mask 图层实心部分会被保留下来,其他则会被抛弃。

例子:// 显示的效果为:以image的形状对subView进行剪切
    self.subView = [[UIView alloc] init];
    self.subView.backgroundColor = [UIColor redColor];
    self.subView.frame = CGRectMake(0, 0, 300, 500);
    [self.view addSubview:self.subView];
    
    CALayer * maskLayer = [CALayer layer];
    UIImage * image = [UIImage imageNamed:@"icon_share"] ;
    maskLayer.frame = CGRectMake(150-image.size.width, 250-image.size.height, image.size.width, image.size.height) ;
    maskLayer.contents = (__bridge id __nullable)image.CGImage;
    maskLayer.contentsGravity = kCAGravityCenter ;
    maskLayer.contentsScale = [UIScreen mainScreen].scale ;
    
    self.subView.layer.mask = maskLayer ;



5. 拉伸过滤介绍。

        简介:相对的两个属性 ;一个是 minificationFilter 属性,用于缩小功能;另一个是 magnificationFilter 属性,用于放大功能。都是 NSString 类型,但都是枚举。设置不同的值有不同的效果,如下功能介绍 :

kCAFilterLinear(默认值):通过对多个像素取样最终生成新的值,得到一个平滑的表现不错的拉伸。但是当放大倍数比较大时图片就模糊不清。

kCAFilterNearest:是一种比较武断的方法,就是取样最近的单像素点而不管其他的颜色,这样做非常快,也不会使图片模糊,但最明显的效果是会使得压缩图片更糟,图片放大后也显得块状或是马赛克严重。

kCAFilterTrilinear:与kCAFilterLinear非常相似,大部分情况下二者都看不出来,但比较而言,三线性滤波算法存储了多个大小情况下的图片(也叫多重贴图),并三维取样,同时结合大图和小图的存储进而得到最后的结果。效果显示清楚。



6. 仿射变换

       简介:在旋转时遵守一个规律,即:右手法则,大母指朝向原点,手握的方向即为旋转的方向。属性 layer.affineTransform 与 view.transform 功能一样。layer.transform 为3D效果。

1)transform 3D效果

        

 1.1:单独变换的一些方法 :

CATransform3DMakeRotation(CGFloat angle, CGFloat x, CGFloat y, CGFloat z);   // 以点(x,y,z)为向量旋转图层angle度

CATransform3DMakeScale(CGFloat sx, CGFloat sy, CGFloat sz);   // 对x、y和z方向上的点分别进行sx、sy和sz倍的缩放

CATransform3DMakeTranslation(Gloat tx, CGFloat ty, CGFloat tz);    // 对layer的x、y和z方向分别平移tx、ty和tz。

1.2: 混合变换的一些方法:

CATransform3DRotate (CATransform3D t,CGFloat angle, CGFloat x,CGFloat y, CGFloat z);    // 在原有的变换基础上以点(x,y,z)为向量旋转图层angle度

CATransform3DScale (CATransform3D t, CGFloat sx,CGFloat sy, CGFloat sz);     // 在原有的基础上对x、y和z方向上的点分别进行sx、sy和sz倍的缩放

CATransform3DTranslation(CATransform3D t,Gloat tx, CGFloat ty, CGFloat tz);     // 在原有变换的基础上对layer的x、y和z方向分别平移tx、ty和tz

CATransform3DConcat (CATransform3D a, CATransform3D b);    // 在两个变换的基础上创建一个新的变换值

2)视图透视投影,立体感 

        简介:视图投影,使得在视觉上会产生错觉有立体感,结合上面的 3D 变换效果 会更好。

2.1 修改m34值使透视投影有立体感:

    2.1.1 、在现实生活中,当物体远离我们的时候,由于视角的原因看起来会变小,理论上远离我们的边要比靠近视角的边更短,但在实际上没有这样显示 ,而我们当前的视角是等距离的,也就是在3D变换中保持平行,和之前提到的仿射类似变换类似。所以为了修改在视觉上的差义性,就需要设置 CATransform3D 的透视效果中和一个元素:m34。m34用于按比例缩放X和Y的值来计算到底要离视图多远。

     2.1.2 、m34的值默认是0,我们可以通过设置 m34 为 -1.0 / D,D来应用透明效果,D代表想像中视角相机和屏幕之前的距离,以你素为单位。实现上我们大概估算就可以了,通常500~1000就已经很好了,D设置非常微的值会让它基本失真,越大的值会让它失去透明效果。( m34用于按比例缩放X和Y的值来计算到底要离视图多远 )。




2.2  3D变换后有立体感设置m34

        2.2.1 :  单视图投影立体感,如下

         

样例:self.imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"registerImageName"]];
    self.imageView.frame = CGRectMake(30, 50, 300, 600);
    [self.view addSubview:self.imageView];
    
    self.imageView.layer.shadowOpacity = 0.5 ;
    self.imageView.layer.shadowRadius = 4 ;
    self.imageView.layer.shadowOffset = CGSizeMake(0, 0);

    // 如果去掉transform3D.m34 = -1.0/500;代码,那么没有投影效果的立体感,只缩小了一点X和Y
    CATransform3D transform3D = CATransform3DIdentity ;
    transform3D.m34 = -1.0/500;
    transform3D = CATransform3DRotate(transform3D, M_PI_4, 1, 0, 0);
    self.imageView.layer.transform = transform3D ;


       2.2.2 :设置多个子视图投影立体感。只需设置 父视图的 superView.layer.sublayerTransform 属性,如下:

       只需要设置 superView.layer.sublayerTransform 子图层变换就会有立体感: 

        CATransform3D transform3D = CATransform3DIdentity ;

        transform3D.m34 = -1.0/500;

        self.view.layer.sublayerTransform = transform3D ;

         

        优点:灭点被设置在容器图层的中点,从而不需要再对子图层分别设置了。这意味着你可以随意使用posintion和frame来放置子图层,而不需要把它们放置在屏幕的中心,然后为了保证统一的灭点用变换来做平移。


样例: self.imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"registerImageName"]];
    self.imageView.frame = CGRectMake(50, 50, 100, 200);
    [self.view addSubview:self.imageView];
    
    self.imageView2 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"registerImageName"]];
    self.imageView2.frame = CGRectMake(150, 50, 100, 200);
    [self.view addSubview:self.imageView2];
    
    self.imageView.layer.shadowOpacity = 0.5 ;
    self.imageView.layer.shadowRadius = 4 ;
    self.imageView.layer.shadowOffset = CGSizeMake(0, 0);
    
    self.imageView2.layer.shadowOpacity = 0.5 ;
    self.imageView2.layer.shadowRadius = 4 ;
    self.imageView2.layer.shadowOffset = CGSizeMake(0, 0);

    // 如果去掉transform3D.m34 = -1.0/500;代码,那么没有投影效果的立体感,只缩小了一点X和Y
    CATransform3D transform3D = CATransform3DIdentity ;
    transform3D.m34 = -1.0/500;
    self.view.layer.sublayerTransform = transform3D ;
    
    self.imageView.layer.transform = CATransform3DMakeRotation(M_PI_4, 0, 1, 0);
    self.imageView2.layer.transform = CATransform3DMakeRotation(-M_PI_4, 0, 1, 0);


 2.3  doubleSided 属性

    表示:是否需要双面显示视图的内容,即背面是否要描绘显示出视图的内容。

例如  layer 旋转180 度 ,然后再设置 该属性后 显示效果。

2.3.1 : doubleSided = YES 在背面描绘显示出视图的内容,视图旋转了M_PI,所有的子视图也旋转了并可以从父视图背面看到,因为系统在背面描绘了内容。浪费CPU。


例子:self.imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"registerImageName"]];
    self.imageView.frame = CGRectMake(50, 50, 100, 200);
    [self.view addSubview:self.imageView];
    
    self.imageView.layer.shadowOpacity = 0.5 ;
    self.imageView.layer.shadowRadius = 4 ;
    self.imageView.layer.shadowOffset = CGSizeMake(0, 0);
    
       // 如果去掉transform3D.m34 = -1.0/500;代码,那么没有投影效果的立体感,只缩小了一点X和Y
//    CATransform3D transform3D = CATransform3DIdentity ;
//    transform3D.m34 = -1.0/500;
//    self.view.layer.sublayerTransform = transform3D ;
    
    self.imageView.layer.transform = CATransform3DMakeRotation(M_PI, 0, 1, 0);
//    self.imageView.layer.doubleSided = NO ;


2.3.2 :  doubleSided = NO (默认值)不在背面描绘显示出视图的内容,视图旋转了M_PI,所有的子视图也旋转了,但不能从父视图背面看到,因为系统不会描绘看原来看不到的内容。不浪费CPU。(背面一片空白的效果)


例子:self.imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"registerImageName"]];
    self.imageView.frame = CGRectMake(50, 50, 100, 200);
    [self.view addSubview:self.imageView];
    
    self.imageView.layer.shadowOpacity = 0.5 ;
    self.imageView.layer.shadowRadius = 4 ;
    self.imageView.layer.shadowOffset = CGSizeMake(0, 0);
    
       // 如果去掉transform3D.m34 = -1.0/500;代码,那么没有投影效果的立体感,只缩小了一点X和Y
//    CATransform3D transform3D = CATransform3DIdentity ;
//    transform3D.m34 = -1.0/500;
//    self.view.layer.sublayerTransform = transform3D ;
    
    self.imageView.layer.transform = CATransform3DMakeRotation(M_PI, 0, 1, 0);
    self.imageView.layer.doubleSided = NO ;


2.4 斜切视图


例子:  视图向x轴方面斜切,A那边属于向外 				    						 - (void)viewDidLoad {
    [super viewDidLoad];

    self.imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"shearTransformImageName"]];
    self.imageView.frame = CGRectMake(50, 50, 200, 400);
    [self.view addSubview:self.imageView];
    
    self.imageView.transform = CGAffineTransformShear(1, 0);
}

CGAffineTransform CGAffineTransformShear(CGFloat x , CGFloat y)
{
    CGAffineTransform transform = CGAffineTransformIdentity ;
    transform.c = -x ;
    transform.b = y ;
    return transform ;
}



7.  CALayer 子类


1)CATransformLayer 变换:是一个专门用来创建三维视图的一个layer,也可以说是多个layer的集合。它没有多余的API,可以这么说,他只是承载了子 layer


例子:
-(CALayer *)faceWithTransform:(CATransform3D)transform
{
    CALayer * layer = [CALayer layer];
    layer.frame = CGRectMake(0, 0, 100, 100);
    
    CGFloat red = (rand()/(double)INT_MAX);
    CGFloat green = (rand()/(double)INT_MAX);
    CGFloat blue = (rand()/(double)INT_MAX);
    layer.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1.0].CGColor;
    
    layer.transform = transform ;
    
    return layer ;
}

-(CATransformLayer *)cubeWithTransform:(CATransform3D)transf frame:(CGRect)frame
{
    // create cube layer
    CATransformLayer * cube = [CATransformLayer layer];
    
    // add cube face 1 前
    CATransform3D transform = CATransform3DMakeTranslation(0, 0, 50);
    [cube addSublayer:[self faceWithTransform:transform]];
    
    // add cube face 2 右
    transform = CATransform3DMakeTranslation(50, 0, 0);
    transform = CATransform3DRotate(transform, M_PI_2, 0, 1, 0);
    [cube addSublayer:[self faceWithTransform:transform]];
    
    // add cube face 3 顶
    transform = CATransform3DMakeTranslation(0,-50 , 0);
    transform = CATransform3DRotate(transform, M_PI_2, 1, 0, 0);
    [cube addSublayer:[self faceWithTransform:transform]];
    
    // add cube face 4 底
    transform = CATransform3DMakeTranslation(0, 50, 0);
    transform = CATransform3DRotate(transform, -M_PI_2, 1, 0, 0);
    [cube addSublayer:[self faceWithTransform:transform]];
    
    // add cube face 5 后
    transform = CATransform3DMakeTranslation(0, 0, -50);
    transform = CATransform3DRotate(transform, M_PI, 1, 0, 0);
    [cube addSublayer:[self faceWithTransform:transform]];
    
    // add cube face 6 左
    transform = CATransform3DMakeTranslation(-50, 0, 0);
    transform = CATransform3DRotate(transform, -M_PI_2, 0, 1, 0);
    [cube addSublayer:[self faceWithTransform:transform]];
    
    // set cube transform
    cube.transform = transf ;
    cube.frame = frame ;
    cube.doubleSided = NO ;
    
    return cube ;
}

- (void)viewDidLoad {
    [super viewDidLoad];

    // 设置统一来点
    CATransform3D sublayerTransform = CATransform3DIdentity ;
    sublayerTransform.m34 = -1.0/500.0;
    self.view.layer.sublayerTransform = sublayerTransform ;
    
    // set up cube 1 add to superLayer and transform
    CATransform3D transf1 = CATransform3DIdentity ;
    transf1 = CATransform3DRotate(transf1, (M_PI_2/90.0)*3, 1, 0, 0);
    transf1 = CATransform3DRotate(transf1, -(M_PI_2/90.0)*10, 0, 1, 0);
    [self.view.layer addSublayer:[self cubeWithTransform:transf1 frame:CGRectMake(100, 100, 100, 100)]];
    
    // set up cube 2 add to superLayer and transform
    CATransform3D transf2 = CATransform3DIdentity ;
    transf2 = CATransform3DRotate(transf2, -(M_PI_2/90.0)*30, 1, 0, 0);
    transf2 = CATransform3DRotate(transf2, (M_PI_2/90.0)*30, 0, 1, 0);
    [self.view.layer addSublayer:[self cubeWithTransform:transf2 frame:CGRectMake(100, 300, 100, 100)]];
}


2)CAShapeLayer画图

      可以用来绘制所有能通过CGPath 来表示的形状。这个图形不一定要闭合,图层的路径也不一定不可破。可以控制 一些属性比如:lineWith(线宽,用点表示单位),lineCap(线条结尾的样子),和lineJoin(线条之前结合点的样子);可以使用UIBezierPath帮助类创建了图层的路径,这样就不用我们考虑人工释放CGPath了。


例子:

    UIBezierPath *path = [UIBezierPath bezierPath];
    [path moveToPoint:CGPointMake(175, 100)];
    [path addArcWithCenter:CGPointMake(150, 100) radius:25 startAngle:0 endAngle:2*M_PI clockwise:YES];
    [path moveToPoint:CGPointMake(150, 125)];
    [path addLineToPoint:CGPointMake(150, 175)];
    [path addLineToPoint:CGPointMake(125, 225)];
    [path moveToPoint:CGPointMake(150, 175)];
    [path addLineToPoint:CGPointMake(175, 225)];
    [path moveToPoint:CGPointMake(100, 150)];
    [path addLineToPoint:CGPointMake(200, 150)];
    
    CAShapeLayer * shapeLayer = [[CAShapeLayer alloc] init];
    shapeLayer.lineWidth = 10 ;
    shapeLayer.lineJoin = kCALineJoinRound;
    shapeLayer.lineCap = kCALineCapRound;
    shapeLayer.strokeColor = [UIColor blueColor].CGColor ;
    shapeLayer.fillColor = [UIColor redColor].CGColor ;
    shapeLayer.path = path.CGPath ;

    [self.view.layer addSublayer:shapeLayer];


3)CATextLayer显示文字

      优点:CATextLayer要比UILabel渲染快得多。


例子:
self.textLayer = [CATextLayer layer];
    self.textLayer.frame = CGRectMake(100, 100, 100, 100);
    [self.view.layer addSublayer:self.textLayer];
    
    // set text attributes
    self.textLayer.foregroundColor = [UIColor redColor].CGColor ;
    self.textLayer.alignmentMode = kCAAlignmentJustified ;
    self.textLayer.wrapped = YES ;
    
    UIFont * font = [UIFont systemFontOfSize:15];
    
    CFStringRef fontName = (__bridge CFStringRef) font.fontName ;
    self.textLayer.font = CGFontCreateWithFontName(fontName);
    self.textLayer.fontSize = font.pointSize ;
    
    NSString * text = @"dfdsafjioweofewfuofjoewhfw3efnkewhofewhweofhweoqfhewhfeowqhfoewfoewofnwwwfoewhfoewihfeoiw";
    self.textLayer.string = text ;




// 不设置contentsScale在Retina屏幕下会像素化,所以加上这个设置
    self.textLayer.contentsScale = [UIScreen mainScreen].scale;


4)CAGradientLayer多颜色平滑渐变

特点:

CAGradientLayer 是用来生成两种或更多颜色平滑渐变的,用 Core Graphics 复制一个 CAGradientLayer 并将内容绘制到一个普通图层的寄宿图也是有可能的,但 CAGradientLayer 的真正好处在于绘制使用了  ”硬件加速” 。也就是说性能快。

startPoint :颜色渐变渲染的起点,范围[0~1.0],按倍数在自身取点;

endPoint   :颜色渐变渲染的终点,范围[0~1.0],按倍数在自身取点;

colors		   :渐变的颜色值,里面是CGColorRef类型(并不是从NSObject派生而来),所以我们要用通过bridge转换以确保编译正常。

locations    :默认情况下,这些颜色在空间上均匀地被染色,但是我们可以用 locations来调整空间。locations数组里的内容属性是一个用NSNumber包装的浮点,范围[0~1.0];这个浮点数定义了colors属性中每个不同颜色的位置,locations数组的大小必需和colors个数大小一样,不然得到一片空白。

............

- (void)viewDidLoad {
    [super viewDidLoad];

    CAGradientLayer * gradientLayer = [CAGradientLayer layer];
    gradientLayer.frame = CGRectMake(100, 100, 100, 100);
    [self.view.layer addSublayer:gradientLayer];

    // set up colors
    gradientLayer.colors = @[(__bridge id)[UIColor redColor].CGColor , (__bridge id)[UIColor blueColor].CGColor];
    // set up start and end point [0~1.0]
    gradientLayer.startPoint = CGPointMake(0, 0);
    gradientLayer.endPoint = CGPointMake(1, 1);
}



5)CAReplicatorLayer  复制子图层

属性:
//拷贝的次数
@property NSInteger instanceCount;
//是否开启景深效果
@property BOOL preservesDepth;
//当CAReplicatorLayer的子Layer层进行动画的时候,拷贝的副本执行动画的延时
@property CFTimeInterval instanceDelay;
//拷贝副本的3D变换
@property CATransform3D instanceTransform;
//拷贝副本的颜色变换
@property(nullable) CGColorRef instanceColor;
//每个拷贝副本的颜色偏移参数
@property float instanceRedOffset;
@property float instanceGreenOffset;
@property float instanceBlueOffset;
//每个拷贝副本的透明度偏移参数
@property float instanceAlphaOffset;


//create a replicator layer and add it to our view
    CAReplicatorLayer * replicatorLayer = [CAReplicatorLayer layer];
    replicatorLayer.position = CGPointMake(self.view.center.x, self.view.center.y - 100);
    replicatorLayer.bounds = CGRectMake(0, 0, 300, 300);
    replicatorLayer.backgroundColor = [UIColor grayColor].CGColor;
    [self.view.layer addSublayer:replicatorLayer];

    // set up replice count
    replicatorLayer.instanceCount = 10 ;

    // set up instanceTransform
    CATransform3D transform = CATransform3DIdentity;
    transform = CATransform3DTranslate(transform, 0, 200, 0);
    transform = CATransform3DRotate(transform, M_PI / 5.0 , 0, 0, 1);
    transform = CATransform3DTranslate(transform, 0, -200, 0);
    replicatorLayer.instanceTransform = transform ;
    
    // 颜色变化
    replicatorLayer.instanceBlueOffset = -0.1;
    replicatorLayer.instanceGreenOffset = -0.1;
    
    // 被复制的子图层
    CALayer * subLayer = [CALayer layer];
    // x , y 值对图形显示有有影响
    subLayer.frame = CGRectMake(100, 100, 100, 100);
    subLayer.backgroundColor = [UIColor greenColor].CGColor;
    
    [replicatorLayer addSublayer:subLayer];


6)CAScrollLayer 滑动图层

        CAScrollLayer与UISrollView和UITableView不同。CAScrollLayer可以无限滑动,但并不负责将触摸事件转换为滑动事件,不渲染滚动条,也不实现任何IOS指定行为例如滑动反弹。

       如果要实现滑动,那就必需添加一个UIPanGestureRecognizer手势实现触摸事件响应。

      

提供了一些属性和方法:

// 调用这个方法可以滑动到指定的位置,方法是从图层树中查找并找到第一个可用的CAScrollLayer,然后滑动它使得指定点成为可视的。 
- (void)scrollPoint:(CGPoint)p;
// 方法实现了同样的事情只不过是作用在一个矩形上的。
  - (void)scrollRectToVisible:(CGRect)r;
// 属性决定图层(如果存在的话)的哪一部分是当前的可视区域。
  @property(readonly) CGRect visibleRect;


7)CATiledLayer 平铺图片

8)CAEmitterLayer创建实时例子动画如:火,烟雾,雨等

属性:

emitterCells :数组里面类型用CAEmitterCell。

CAEMitterCell 一些属性 :
     color :指定了一个可以混合图片内容颜色的混合。
     emissionRange :属性的范围变化。属性的值为2*M_PI,意味着例子可以从360度任意位置反射出来。如果指定一个小一些的值,可以创造出一个圆锥形。
     alphaSpeed :指定值在时间线上的变化。值为-0.4时,就是说明例子的透明度每过一秒就是减少0.4,这样就有发射出去之后逐渐小时的效果。

preservesDepth:是否将3D例子系统平面化到一个图层(默认值)或者可以在3D空间中混合其他的图层。

renderMode :控制着在视觉上粒子图片是如何混合的。
值和效果:
kCAEmitterLayerAdditive:合并例子重叠部分的亮度使得看上去更亮。


例子:
- (void)viewDidLoad {
    [super viewDidLoad];

    //create particle emitter layer
    CAEmitterLayer *emitter = [CAEmitterLayer layer];
    emitter.frame = CGRectMake(100, 100, 100, 100);
    [self.view.layer addSublayer:emitter];
    
    //configure emitter
    emitter.renderMode = kCAEmitterLayerAdditive;
    emitter.emitterPosition = CGPointMake(emitter.frame.size.width / 2.0, emitter.frame.size.height/2.0);
    
    //create a particle template
    CAEmitterCell *cell = [[CAEmitterCell alloc] init];
    cell.contents = (__bridge id)[UIImage imageNamed:@"ic_message"].CGImage;
    cell.birthRate = 150;
    cell.lifetime = 5.0;
    cell.color = [UIColor colorWithRed:1 green:0.5 blue:0.1 alpha:1.0].CGColor; cell.alphaSpeed = -0.4;
    cell.velocity = 50;
    cell.velocityRange = 50;
    cell.emissionRange = M_PI * 2.0;
    
    //add particle template to emitter
    emitter.emitterCells = @[cell];
}


9) CAEAGLLayer: 用来显示任意的OpenGL(高性能图形绘制)图形

      OpenGL介绍:提供了Core Animation的基础,它是底层的C接口,直接和iPhone 、iPad的硬件通信,极少地抽象出来方法。OpenGL没有对象或是图层的概念。它只是简单地处理三角形。

      IOS5中,苹果引入了一个新的框架叫做GLKit,它去掉了一些OpenGL的复杂特性,提供一个叫做CLKView的UIView子类,帮你处理大部分的设置和绘制工作。  前提是   各种各样的OpenGL绘图缓冲的底层可配置项仍然需要你们CAEGLLayer完成,用来显示任总的OpenGL图形。



10)AVPlayerLayer 播放视频#import <AVFoundation/AVFoundation.h>

       简单使用:可以用+playerLayerWithPlayer:  方法创建一个已经绑定了视频播放的图层,或者可以先创建一个图层,然后用player属性绑定一个AVPlayer实例。

例子:可以播放一个音乐,但不能播放网络音乐
- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSURL * url = [[NSBundle mainBundle] URLForResource:@"playerMuzice.mp3" withExtension:nil];
    
    AVPlayer * player = [AVPlayer playerWithURL:url];
    AVPlayerLayer * playerLayer = [AVPlayerLayer playerLayerWithPlayer:player];
    
    playerLayer.frame = CGRectMake(0, 0, 100, 100);
    playerLayer.position = self.view.center ;
    [self.view.layer addSublayer:playerLayer];
    
    [player play];
}





二、事务管理动画


1. CATransaction管理事务可以修改属性做动画

1) 隐式动画主要是作用于CALayer可动画属性上面,UIView对应的layer是不可以的,只要你改变属性的值,它不是突兀的直接改变过去,而是一个有一个动画的过程,这个时间等属性你可以通过事务(CATransaction)来控制,如果你不自己提供一个事务,它的默认时间是0.25秒,当然这个可动画属性是需要触发的,如果你一上来就设置一个值,可能看不到动画效果.

        事务实际上是Core Animation用来包含一系列属性动画集合的机制,任何用指定事务去改变可以做动画的图层属性不会立马发生变化,而是当事务一旦提交的时候开始用一个动画过渡到新值。比如:置一个背景颜色。

        事务是通过CATransaction来做管理,这个类的设计有些奇怪,不像你从它的命名预期的那样去管理一个简单的事务,而一叠你不能访问的事务。CATransaction并没有属性或者实例方法,并且也不能用+alloc 和 -init 方法创建它。但可以用+begin 和 +commit 分别来入栈或者出栈。

        任何可以做动画的图层属性都会被添加到栈顶的事务,可以通过+setAnimationDuration:方法设置当前事务的动画时间,或者通过+animationDuration方法来获取值(默认0.25秒)

 

2)UIView对应的layer默认是不能做隐式动画的,但只需要实现那个layer.delegate的- (nullable id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event;方法返回一个CAAction字典。

还有一种就是通过CATransaction的实例,叫做推进过渡动画

( 

        即平滑过渡,可以对图层任何变化平滑过渡

 )CATransition。


//add a custom action

 CATransition *transition = [CATransition animation];

// 从边沿的一测滑动进入,把旧的从另一侧推出去

 transition.type = kCATransitionPush;

// 从左侧滑入

 transition.subtype = kCATransitionFromLeft;

 self.colorLayer.actions = @{@"backgroundColor": transition};


3)UIView有两种方式来作动画,但这两种表面不同,实际上内部都是调用事务 CATransaction的 +begin和+commit方法做的:

1、+beginAnimations:context: 和 +commitAnimations

2、+animateWithDuration:animations:这样可以避免开发者失误造成的风险


4)

例如实现CALyaer背景色平滑变色(隐式动画):

@interface ViewController ()

@property (nonatomic , strong)CALayer * bgColorLayer ;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    self.bgColorLayer = [CALayer layer];
    self.bgColorLayer.backgroundColor = [UIColor blueColor].CGColor;
    self.bgColorLayer.frame = CGRectMake(100, 100, 100, 100);
    [self.view.layer addSublayer:self.bgColorLayer];
    
    UIButton * btn = [[UIButton alloc] initWithFrame:CGRectMake(100, 210, 100, 50)];
    [btn setTitle:@"随机改变颜色" forState:UIControlStateNormal];
    [btn setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
    [btn addTarget:self action:@selector(changeColor) forControlEvents:UIControlEventTouchUpInside];
    
    [self.view addSubview:btn];
}

-(void)changeColor
{
    [CATransaction begin];
    
    [CATransaction setAnimationDuration:1.0];
    
    CGFloat red = (arc4random()%256)*1.0/255.0 ;
    CGFloat green = (arc4random()%256)*1.0/255.0 ;
    CGFloat blue = (arc4random()%256)*1.0/255.0 ;
    self.bgColorLayer.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1.0].CGColor;
    
    [CATransaction commit];
}

5)完成块

      执行完指定的动画后再执行完成动画后的block,但注意完成动画后需要执行的block代码位置要放在设置动画代码的前面。如果位置放在最后,那么效果会先执行block再执行动画,因为事务就像一个栈,先进后出,即先进后执行。

例子先改变完颜色后执行block里面的内容:

@interface ViewController ()

@property (nonatomic , strong)CALayer * bgColorLayer ;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    self.bgColorLayer = [CALayer layer];
    self.bgColorLayer.backgroundColor = [UIColor blueColor].CGColor;
    self.bgColorLayer.frame = CGRectMake(100, 100, 100, 100);
    [self.view.layer addSublayer:self.bgColorLayer];
    
    UIButton * btn = [[UIButton alloc] initWithFrame:CGRectMake(100, 210, 100, 50)];
    [btn setTitle:@"随机改变颜色" forState:UIControlStateNormal];
    [btn setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
    [btn addTarget:self action:@selector(changeColor) forControlEvents:UIControlEventTouchUpInside];
    
    [self.view addSubview:btn];
}

-(void)changeColor
{
    [CATransaction begin];
    
    [CATransaction setAnimationDuration:1.0];
    
    // 完成颜色改变后调用block,才会在改变颜色完成后正确调用block
    [CATransaction setCompletionBlock:^{
        CGAffineTransform transform = self.bgColorLayer.affineTransform ;
        transform = CGAffineTransformRotate(transform, M_PI_4);
        self.bgColorLayer.affineTransform = transform ;
    }];
    
    CGFloat red = (arc4random()%256)*1.0/255.0 ;
    CGFloat green = (arc4random()%256)*1.0/255.0 ;
    CGFloat blue = (arc4random()%256)*1.0/255.0 ;
    self.bgColorLayer.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1.0].CGColor;
    
    // 完成后调用的block放在这个位置的话会先执行,然后再实现上面的动画
//    [CATransaction setCompletionBlock:^{
//        CGAffineTransform transform = self.bgColorLayer.affineTransform ;
//        transform = CGAffineTransformRotate(transform, M_PI_4);
//        self.bgColorLayer.affineTransform = transform ;
//        
//        transform = self.bgColorLayer.affineTransform ;
//        transform = CGAffineTransformRotate(transform, M_PI_4);
//        self.bgColorLayer.affineTransform = transform ;
//    }];
    
    [CATransaction commit];


2. CAAnimation<CAMediaTiming>动画

1)部分属性:timingFunction 类型是CAMediaTimingFunction,设置动画缓冲,默认为nil。


2)给layer图层添加动画

2.1、CAKeyframeAnimation 关键帧动画

// 改变颜色
- (IBAction)changeColor
  {
      //create a keyframe animation
      CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
      animation.keyPath = @"backgroundColor";
      animation.duration = 2.0;
      animation.values = @[
                           (__bridge id)[UIColor blueColor].CGColor,
                           (__bridge id)[UIColor redColor].CGColor,
                           (__bridge id)[UIColor greenColor].CGColor,
                           (__bridge id)[UIColor blueColor].CGColor ];
      //apply animation to layer
      [self.colorLayer addAnimation:animation forKey:nil];
  }


2.1.2、 CAKeyframeAnimation、CAShapeLayer 和 UIBezierPath用法创建动画

效果是那个三角纸飞机的position(中点)会沿着曲线滑动,持续时间是4s。

#import "ViewController.h"

@interface ViewController ()

@property (nonatomic , strong) UIView * contentView ;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width ;
    CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height ;

    self.contentView = [[UIView alloc] initWithFrame:CGRectMake(0, 0,screenWidth , screenHeight)];
    self.contentView.backgroundColor = [UIColor blackColor];
    [self.view addSubview:self.contentView];
    
    
    UIBezierPath * bezierPath = [UIBezierPath bezierPath];
    [bezierPath moveToPoint:CGPointMake(0, 150)];
    [bezierPath addCurveToPoint:CGPointMake(300, 150) controlPoint1:CGPointMake(300.0*1/4, 0) controlPoint2:CGPointMake(300.0*3/4, 300)];
    
    CAShapeLayer * shapeLayer = [CAShapeLayer layer];
    shapeLayer.path = bezierPath.CGPath ;
    shapeLayer.lineWidth = 3.0 ;
//    shapeLayer.backgroundColor = [UIColor grayColor].CGColor ;
    shapeLayer.fillColor = [UIColor blueColor].CGColor ;
    shapeLayer.strokeColor = [UIColor redColor].CGColor ;
    [self.contentView.layer addSublayer:shapeLayer];
    
    // 添加一个滑动的图层
    CALayer * slipLayer = [CALayer layer];
    slipLayer.frame = CGRectMake(0, 0, 50, 50);
    slipLayer.position = CGPointMake(0, 150);
    slipLayer.contents = (__bridge id)[UIImage imageNamed:@"icon_tmpImageName"].CGImage ;
    slipLayer.affineTransform = CGAffineTransformRotate(CGAffineTransformIdentity, M_PI_4);
    [self.contentView.layer addSublayer:slipLayer];
    
    // 根据position创建一个(图层的中心点)关建帧动画,
    CAKeyframeAnimation * keyframeAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    keyframeAnimation.duration = 4.0 ;
    keyframeAnimation.path = bezierPath.CGPath ;
    keyframeAnimation.repeatCount = 100 ;
    // 把rotationMode设置=kCAAnimationRotateAuto 根据曲线的切线自动旋转
    keyframeAnimation.rotationMode = kCAAnimationRotateAuto ;
    
    // slipLayer 添加一个动画
    [slipLayer addAnimation:keyframeAnimation forKey:nil];
    
    
}


2.2 、CAPropertyAnimation属性动画

 CAPropertyAnimation提供了如下方法来创建属性动画。

         + (instancetype)animationWithKeyPath:(NSString *)path;该方法仅需要一个参数,该参数只是一个字符串的值,指定CALayer的动画属性名,该设置属性动画控制CALayer的哪个动画属性持续的改变。

 

除此之外,CAPropertyAnimation还支持如下属性:

    keyPath:该属性值返回创建CAPropertyAnimation时指定的参数。

    additive: 该属性指定该属性动画是否以当前动画效果为基础。

    cumulative:该属性指定动画是否为累加效果。

    valueFunction: 该属性值是一个CAValueFunction对象,该对象负责对属性改变的插值计算,系统已经提供了默认的插值计算方式,因此一般无须指定该属性。



2.3 、CABasicAnimation做旋转的例子(使用虚拟属性变换)

运行效果特别好,用transform.rotation而不是transform做动画的好处如下:

(1).我们可以不通过关键帧一步旋转多于180度的动画。

(2).可以用相对值而不是绝对值(设置byValue而不是toValue)。

(3).可以不用创建CATransform3D,而是使用一个简单的数值来指定角度。

(4).不会和transform.position或者transform.scale冲突(同样是使用关键路径来做独立的动画属性)。


transform.rotation 属性有一个奇怪的问题是它其实并不存在。这是因为 CATransform3D 并不是一个对象,它实际是一个结构体,也没有符合 KVC 相关的属性,transform.rotation 实际上是一个 CALayer 用于处理动画变换的虚拟属性。


//
//  ViewController.m
//  CAKeyframeAnimation--+CAShapeLayer+UIBezierPath用法
//
//  Created by 瞿杰 on 2017/3/24.
//  Copyright © 2017年 yiniu. All rights reserved.
//

#import "ViewController.h"

@interface ViewController ()

@property (nonatomic , strong) UIView * contentView ;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width ;
    CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height ;

    self.contentView = [[UIView alloc] initWithFrame:CGRectMake(0, 0,screenWidth , screenHeight)];
    self.contentView.backgroundColor = [UIColor blackColor];
    [self.view addSubview:self.contentView];

    
    // 添加一个滑动的图层
    CALayer * slipLayer = [CALayer layer];
    slipLayer.frame = CGRectMake(0, 0, 50, 50);
    slipLayer.position = CGPointMake(160, 150);
    slipLayer.contents = (__bridge id)[UIImage imageNamed:@"icon_tmpImageName"].CGImage ;
    slipLayer.affineTransform = CGAffineTransformRotate(CGAffineTransformIdentity, M_PI_4);
    [self.contentView.layer addSublayer:slipLayer];

    
    // 做旋转功能
    CABasicAnimation * basicAnimation = [CABasicAnimation animation];
    basicAnimation.keyPath = @"transform.rotation";
    basicAnimation.repeatCount = 100 ;
    basicAnimation.duration = 3.0;
    basicAnimation.byValue = @(M_PI * 2);
    [slipLayer addAnimation:basicAnimation forKey:nil];
    
}


2.4 、CAAnimationGroup 动画组

          CAAnimationGroup是另一个继承于CAAnimation的子类,它添加了一个animations数组的属性,用来组合别的动画。


//
//  ViewController.m
//  CAAnimationGroup-动画组
//
//  Created by 瞿杰 on 2017/3/26.
//  Copyright © 2017年 yiniu. All rights reserved.
//

#import "ViewController.h"

@interface ViewController ()

@property (nonatomic,strong) UIView * contentView ;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.contentView = [[UIView alloc] initWithFrame:CGRectMake(10, 50, 300, 300)];
    self.contentView.backgroundColor = [UIColor brownColor];
    [self.view addSubview:self.contentView];
    
    // 1.设置曲线
    UIBezierPath * bezierPath = [UIBezierPath bezierPath];
    [bezierPath moveToPoint:CGPointMake(0, 150)];
    [bezierPath addCurveToPoint:CGPointMake(300, 150) controlPoint1:CGPointMake(75, 300) controlPoint2:CGPointMake(225, 0)];
    
    CAShapeLayer * shpaeLayer = [CAShapeLayer layer];
    shpaeLayer.path = bezierPath.CGPath ;
    shpaeLayer.fillColor = [UIColor blueColor].CGColor;
    shpaeLayer.strokeColor = [UIColor redColor].CGColor ;
    shpaeLayer.lineWidth = 3.0;
    [self.contentView.layer  addSublayer:shpaeLayer];
    
    // 2.设置颜色渐变的Layer
    CALayer * colorLayer = [CALayer layer];
    colorLayer.frame = CGRectMake(0, 0, 50, 50);
    colorLayer.position = CGPointMake(0, 150);
    colorLayer.backgroundColor = [UIColor greenColor].CGColor ;
    [self.contentView.layer addSublayer:colorLayer];

    
    // 3.添加动画
    // 3.1帧动画,沿bezierPath.CGPath路径滑动
    CAKeyframeAnimation * keyAnimation = [CAKeyframeAnimation animation];
    keyAnimation.path= bezierPath.CGPath ;
    keyAnimation.keyPath = @"position";
    keyAnimation.rotationMode = kCAAnimationRotateAuto ;
    
    // 3.2改变颜色
    CABasicAnimation * colorAnimation = [CABasicAnimation animation];
    colorAnimation.keyPath = @"backgroundColor";
    colorAnimation.toValue = (__bridge id)[UIColor blueColor].CGColor ;
    
    // 3.3创建一个动画组容器
    CAAnimationGroup * animationGroup = [CAAnimationGroup animation];
    animationGroup.animations = @[keyAnimation,colorAnimation];
    animationGroup.duration = 4.0 ;
    animationGroup.repeatCount = 100 ;
    
    // 3.4添加动画
    [colorLayer addAnimation:animationGroup forKey:nil];
    
}



2.5、CATransition过渡动画


UIView 也可以做过渡动画,具体请看UIView里面的介绍笔记。

效果:当点击图片后,当前的那个图片渐变到没有,同时另一张从没有渐变到完成显示。
#import "ViewController.h"

@interface ViewController ()

@property (nonatomic , strong) UIImageView * imageView ;
@property (nonatomic , strong) NSArray * images ;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    self.imageView = [[UIImageView alloc] init];
    self.imageView.frame = CGRectMake(100, 100, 100, 100);
    self.imageView.userInteractionEnabled = YES ;
    [self.imageView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(switchImage)]];
    [self.view addSubview:self.imageView];
    
    self.images = @[[UIImage imageNamed:@"icon_xl"],
                    [UIImage imageNamed:@"nav_more"],
                    [UIImage imageNamed:@"slider_table"],
                    ];
    self.imageView.image = self.images[0];

}

-(void)switchImage
{
   // 1.用CATransition做过渡动画
    CATransition * transition = [CATransition animation];
    transition.duration = 1.0;
    transition.type = kCATransitionFade ;
    transition.subtype = kCATransitionFromLeft ;
    [self.imageView.layer addAnimation:transition forKey:nil];
    
    // cycle to next image
    UIImage * currentImage = self.imageView.image ;
    NSInteger index = [self.images indexOfObject:currentImage];
    index = (index + 1)%[self.images count];
    self.imageView.image = self.images[index];
}



3)移除layer图层的动画

3.1、在动画进行中时移除动画:

// 移除layer的某一属性或虚拟属性名 设置的动画

- (void)removeAnimationForKey:(NSString *)key; 

// 移除layer图层所有的动画

- (void)removeAllAnimations;


3.2、在动画结束后移除动画

当动画结束后系统会自动移除动画;除非设置 layer.removedOnCompletion = NO ,只有当需要移时手动删除动画,否则它会一直存在于内存中直到图层被销毁。



三、动画 缓冲功能CAMediaTimingFunction

1)系统动画缓冲

1.1 构造方法:+timingFunctionWithName:(NSString *)name; 

name可以传入的参数:

  kCAMediaTimingFunctionLinear

  kCAMediaTimingFunctionEaseIn

  kCAMediaTimingFunctionEaseOut

  kCAMediaTimingFunctionEaseInEaseOut

  kCAMediaTimingFunctionDefault


kCAMediaTimingFunctionLinear 选项创建了一个线性的计时函数,同样也是CAAnimation的timingFunction属性为空时候的默认函数。线性步调使动画立即加速到最大速度并且保持匀速达到终点。


kCAMediaTimingFunctionEaseIn 常量创建了一个开始慢慢加速然后达到最大速度后保持匀速达到终点的过程。


kCAMediaTimingFunctionEaseOut 与上面那个常量效果相反,一开始立即加速到最大速度,然后到终点之前开始减速的过程。


kCAMediaTimingFunctionEaseInEaseOut  创建一个慢慢加速到最大速度然后并持匀速,最后到终点之前开始慢慢减速的过程。是大多数默认的选择。实际上当使用UIView的动画方法时,它的确是默认的,但当创建 CAAnimation 的时候,就需要手动设置它了。


kCAMediaTimingFunctionDefault ,它和kCAMediaTimingFunctionEaseInEaseOut很类似,但是加速和减速的过程稍微有些慢。它和kCAMediaTimingFunctionEaseInEaseOut的区别很难察觉,可能苹果觉得kCAMediaTimingFunctionEaseInEaseOut 对于隐式动画来说更合适(然后对UIKit就改变想法,而是使用kCAMediaTimingFunctionEaseInEaseOut 做为默认效果),虽然它的名字说是默认的,但还是要记住当创建显示的 CAAnimation 动画默认是用 kCAMediaTimingFunctionDefault作为它们的计时方法。



1.2、例子


例子:效果是:三角形图 先 慢慢的加速,然后达到最大速度后匀速,最后快点终点减速。
#import "ViewController.h"

@interface ViewController ()

@property (nonatomic , strong) UIView * contentView ;

@property (nonatomic , strong) CALayer * slipLayer ;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width ;
    CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height ;

    self.contentView = [[UIView alloc] initWithFrame:CGRectMake(0, 0,screenWidth , screenHeight)];
    self.contentView.backgroundColor = [UIColor blackColor];
    [self.view addSubview:self.contentView];
    
    
    UIBezierPath * bezierPath = [UIBezierPath bezierPath];
    [bezierPath moveToPoint:CGPointMake(0, 150)];
    [bezierPath addCurveToPoint:CGPointMake(300, 150) controlPoint1:CGPointMake(300.0*1/4, 0) controlPoint2:CGPointMake(300.0*3/4, 300)];
    
    CAShapeLayer * shapeLayer = [CAShapeLayer layer];
    shapeLayer.path = bezierPath.CGPath ;
    shapeLayer.lineWidth = 3.0 ;
//    shapeLayer.backgroundColor = [UIColor grayColor].CGColor ;
    shapeLayer.fillColor = [UIColor blueColor].CGColor ;
    shapeLayer.strokeColor = [UIColor redColor].CGColor ;
    [self.contentView.layer addSublayer:shapeLayer];
    
    // 添加一个滑动的图层
    CALayer * slipLayer = [CALayer layer];
    slipLayer.frame = CGRectMake(0, 0, 50, 50);
    slipLayer.position = CGPointMake(0, 150);
    slipLayer.contents = (__bridge id)[UIImage imageNamed:@"icon_tmpImageName"].CGImage ;
    slipLayer.affineTransform = CGAffineTransformRotate(CGAffineTransformIdentity, M_PI_4);
    [self.contentView.layer addSublayer:slipLayer];
    self.slipLayer = slipLayer;
    
    // 知识点1:
    // 根据position创建一个(图层的中心点)关建帧动画,
    CAKeyframeAnimation * keyframeAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    keyframeAnimation.duration = 4.0 ;
    keyframeAnimation.path = bezierPath.CGPath ;
    keyframeAnimation.repeatCount = 100 ;
    // 把rotationMode设置=kCAAnimationRotateAuto 根据曲线的切线自动旋转
    keyframeAnimation.rotationMode = kCAAnimationRotateAuto ;
    
    // 手动创建一个缓冲方式
    keyframeAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
//    keyframeAnimation.speed = 2.0 ;
    // slipLayer 添加一个动画
    [slipLayer addAnimation:keyframeAnimation forKey:nil];
    
    
}


2)自定义动画缓冲(使用三次贝赛尔曲线 方式)

2.1、构造方法:

        + (instancetype)functionWithControlPoints:(float)c1x :(float)c1y :(float)c2x :(float)c2y;  

或 初始化方法 - (instancetype)initWithControlPoints:(float)c1x :(float)c1y :(float)c2x :(float)c2y;

提示:这些参数范围(0.0 ~ 1.0),点(c1x,c1y)代表第一个控制点,点(c2x,c2y)代表第二个控制点。cx 是动画的时间比例,cy是动画改变的量,斜率是动画的速度。


2.2、三次贝塞尔曲线 动画 缓冲函数


        CAMediaTimingFunction函数的主要原则在于它把输入的时间转换成起点和终点之间成比例的改变(范围  0.0 ~ 1.0)。我们可以用一个简单的图标来解释,横轴代表时间(范围  0.0 ~ 1.0),纵轴代表改变的量(范围  0.0 ~ 1.0),斜率表示动画的速度,于是线性的缓冲kCAMediaTimingFunctionLinear就是一条从起点开始简单的斜线。

             如图,point1点代表  ( c1x , c1y ) 控制点,point2点代表 ( c2x , c2y ) 控制点 。如果一个动画的 timingFunction属性赋值了同CAMediaTimingFunction 构造的三次贝赛尔曲线缓冲对像,那么该动画会: 一开始很快的加速,然后在中间的一段动画会减速,最后快到终点的一段动画再加速(即 快 --> 慢 --> 快);


2.3、获取缓冲方式的控制点 ,idx 范围包含 0 ~ 3 ,ptr CGPoint 的结构体 - (void)getControlPointAtIndex:(size_t)idx values:(float[2])ptr;

//不知道为什么pointControll1 和 pointControll2 取出的值作用在渲染图层上没有效果。

#import "ViewController.h"

@interface ViewController ()

@property (nonatomic , strong) UIView * timingFunctionPathView ;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    // 1.添加view
    self.timingFunctionPathView = [[UIView alloc] initWithFrame:self.view.bounds];
    self.timingFunctionPathView.backgroundColor = [UIColor greenColor];
    [self.view addSubview:self.timingFunctionPathView];
    
    // 2.创建动画缓冲对象
    CAMediaTimingFunction * timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    
    // 3.获取timingFunction缓冲对象的控制点1 pointControll1 和 控制点2 pointControll2
    CGPoint pointControll1 , pointControll2 ;
//    pointControll1 = CGPointMake(0.25, 0.0);
//    pointControll2 = CGPointMake(0.75, 1.0);
    [timingFunction getControlPointAtIndex:1 values:(float *)&pointControll1];
    [timingFunction getControlPointAtIndex:2 values:(float *)&pointControll2];

    
    // 4.创建 UIBezierPath 把缓冲图画出来
    UIBezierPath * path = [UIBezierPath bezierPath];
    [path moveToPoint:CGPointZero];
    [path addCurveToPoint:CGPointMake(1.0, 1.0) controlPoint1:pointControll1 controlPoint2:pointControll2];
    // 4.1整体路径 paht 放大两200倍,
    [path applyTransform:CGAffineTransformMakeScale(200.0, 200.0)];
    
    
    // 5.把缓冲图的path绘制到CAShapeLayer上并添加到view.layer上显示出来
    CAShapeLayer * shapeLayer = [CAShapeLayer layer];
//    shapeLayer.frame = CGRectMake(100, 100, 100, 100);
    shapeLayer.backgroundColor = [UIColor blackColor].CGColor;
    shapeLayer.path = path.CGPath ;
    shapeLayer.lineWidth = 4 ;
    shapeLayer.strokeColor = [UIColor redColor].CGColor ;
    shapeLayer.fillColor = [UIColor clearColor].CGColor ;
    shapeLayer.fillMode = kCAFillModeBoth ;
    [self.timingFunctionPathView.layer addSublayer:shapeLayer];
    
    // 6.翻转整个 timingFunctionPathView 视图 的 子视图 和 图层,即左上角变成右下角方向对翻,意为让人以正常的视角去看曲线变化
    self.timingFunctionPathView.layer.geometryFlipped = YES ;

}


3) 基于关键帧的缓冲

3.1、改变颜色的例子


例子:改变图层的颜色,设置先由blackColor -> redColor -> greenColor -> blueColor , 整个都是按kCAMediaTimingFunctionEaseInEaseOut方式缓冲做动画。

#import "ViewController.h"

@interface ViewController ()

@property (nonatomic , strong) CALayer * colorLayer ;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    self.colorLayer = [CALayer layer];
    self.colorLayer.frame = CGRectMake(100, 100, 200, 200);
    self.colorLayer.backgroundColor = [UIColor brownColor].CGColor ;
    
    [self.view.layer addSublayer:self.colorLayer];
    
}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    self.colorLayer.backgroundColor = [UIColor blueColor].CGColor ;
    [self animation];
}

-(void)animation
{
    // 1.创建动画
    CAKeyframeAnimation * keyframeAniation = [CAKeyframeAnimation animation];
    keyframeAniation.keyPath = @"backgroundColor";
    keyframeAniation.duration = 4.0 ;
    
    // 2.设置keyPath 变换的值
    keyframeAniation.values = @[
                                (__bridge id)[UIColor blackColor].CGColor ,
                                (__bridge id)[UIColor redColor].CGColor ,
                                (__bridge id)[UIColor greenColor].CGColor ,
                                (__bridge id)[UIColor blueColor].CGColor
                                ];
    
    // 3.设置动画的缓冲方式(即改变动画的速度),EaseInEaseOut 表示变化:先慢变化, 后以最大速度匀速变化,最后减速变化
    CAMediaTimingFunction * fun = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    
    // 4.timingFunctions.count 设置成 values.count - 1 的大小,如果后面再多余的缓冲是没有用的
    keyframeAniation.timingFunctions = @[fun , fun ,fun];
    
//    keyframeAniation.keyTimes = @[@0.3 , @0.7 , @0.95 , @1.0 ];
    
    [self.colorLayer addAnimation:keyframeAniation forKey:nil];
}


3.2、弹弹球例子

例子:弹弹球弹的效果为  从开始位置 到最 高的位置,然后就像现实的弹球反弹的效果差不多,上下弹动,弹的时间点越长,上升的高度越小。

//
//  ViewController.m
//  缓冲方式与关键帧动画--弹弹球
//
//  Created by 瞿杰 on 2017/3/30.
//  Copyright © 2017年 yiniu. All rights reserved.
//

#import "ViewController.h"

@interface ViewController ()

@property (nonatomic , strong) UIView * contentView ;
@property (nonatomic , strong) CALayer * ballLayer ;


@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    
    self.contentView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 400, 400)];
    self.contentView.backgroundColor = [UIColor grayColor];
    [self.view addSubview:self.contentView];
    
    self.ballLayer = [CALayer layer];
    self.ballLayer.backgroundColor = [UIColor blueColor].CGColor;
    self.ballLayer.bounds = CGRectMake(0, 0, 60, 60);
    self.ballLayer.position = CGPointMake(150, 400 - 30);
    self.ballLayer.cornerRadius = 30 ;
    self.ballLayer.masksToBounds = YES ;
    [self.contentView.layer addSublayer:self.ballLayer];

}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    [self animation];
}

-(void)animation
{
    CAKeyframeAnimation * keyframeAnimation = [CAKeyframeAnimation animation];
    keyframeAnimation.duration = 10.0 ;
    keyframeAnimation.keyPath = @"position";

    
    // 从最后一个位置index = count - 1 的值取来做为第一个动画的起始,然后取第2个值所在的位置 index--
    keyframeAnimation.values = @[
                                 [NSValue valueWithCGPoint:self.ballLayer.position],
                                 [NSValue valueWithCGPoint:CGPointMake(150, 50)],
                                 [NSValue valueWithCGPoint:self.ballLayer.position],
                                 [NSValue valueWithCGPoint:CGPointMake(150, 100)],
                                 [NSValue valueWithCGPoint:self.ballLayer.position],
                                 [NSValue valueWithCGPoint:CGPointMake(150, 200)],
                                 [NSValue valueWithCGPoint:self.ballLayer.position],
                                 [NSValue valueWithCGPoint:CGPointMake(150, 268)],
                                 [NSValue valueWithCGPoint:self.ballLayer.position],
                                ];
    
    
    //从 index = 0 位置开始取每一段帧动画的缓冲方式,比如第一段上升动画的缓冲为: kCAMediaTimingFunctionLinear ,timingFunctions.count = values.count - 1 ;
    keyframeAnimation.timingFunctions = @[
                                          [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear],
                                          
                                          [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut],
                                          
                                          [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut],
                                          
                                          [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut],
                                          
                                          [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut],
                                          
                                          [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut],
                                          
                                          [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut],
                                          
                                          [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut],

                                          ];
    
    // 从 index = 0 位置开始取每一段帧动画的执行时间,比如第一段上升动画取差值: 0.1 - 0.0 = 0.1 s ,keyTimes.count = values.count ;
    keyframeAnimation.keyTimes = @[@0.0 , @0.1 , @0.2, @0.35, @0.45, @0.6, @0.8, @0.9 , @1.0];
    
    
    [self.ballLayer addAnimation:keyframeAnimation forKey:nil];
    

//    self.contentView.layer.geometryFlipped = YES ;
}





四、CAMediaTiming 动画 协议

1、 CAMediaTiming 协议定义了在一段动画内用来控制逝去时间的属性集合,CALayer 和 CAAnimation 都实现了这个协议,所以时间可以被任意基于一个图层或一段动画的类制器。


2、协议的属性及功能

duration :一次动画持续的时间,单位(秒),默认0,但不等于没有执行动画。

repeatCount :重复次数,默认0,但不等于没有执行动画。

repeatDuration :让动画重复一个指定的时间,而不是指定次数。

autoreverses(BOOL类型):在每次间隔交替循环过程中自动回放,对于播放一段连续非循环的动画很有用,例如打开一扇门。

fillMode 是 NSString 类型,控制动画完成后做的事 :值:
 kCAFillModeForwards//动画结束后回到准备状态
 kCAFillModeBackwards//动画结束后保持最后状态
 kCAFillModeBoth//动画结束后回到准备状态,并保持最后状态
 kCAFillModeRemoved//执行完成移除动画




相对时间的属性:
      * beginTime 要与 fillMode 一起使用才有效果 : 指定了动画开始之前的延迟时间,默认是0(即动画立即开始)。

      * speed   是时间的倍数,默认为1.0 ,减少它会减慢图层或动画时间,增加它会加快速度。如果2.0是速度,那么对一个duration为1的动画,实际上在0.5秒的时候就已经完成了。

      * timeOffset :    和beginTime类似,但增加beginTime导致的延迟动画不同,增加timeOffset只会让动画快进到某个点,
         例如,对于一个持续1秒的动画来说,设置timeOffset为0.5s意味着动画将从一半的地方开始(在一半的地方开始就是说动画一开始,立即在一半的地方开始执行动画,那么一开始停在一半的效果上与正常执一半的效果一样)。那么当该动画执行到正常下一次结束的位置接着把剩于的时间循环执行完成到一半的效果。当动画结束后恢复所在的位置(因为动画移除了)。
         和beginTime不同的是,timeOffset并不受speed影响。


2.1、手动动画(利用timeOffset)

在设置一个门的时候只需要设置3点就可以做成:

(1) 给一个doorLayer 设置一个动画并添加到doorLayer上,也就相当于固定了路径;

(2) 设置doorLayer.speed = 0 ;

(3) 在doorLayer或所在的view上添加一个滑动手势,然后在滑动手势的目标方法里,根据滑动的距离差值/总长*动画时间 与 doorLayer.timeOffset 比较取最大



效果是图三角形随我向右滑动则在原来的基础上向右沿曲线滑动,我向左滑动则三角形在原来的基础上向左滑动,滑动可以有循环效果。

#import "ViewController.h"

@interface ViewController ()

@property (nonatomic , strong) UIView * contentView ;

@property (nonatomic , strong) CALayer * slipLayer ;
@property (nonatomic , assign) CGFloat startX ;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width ;
    CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height ;

    self.contentView = [[UIView alloc] initWithFrame:CGRectMake(0, 0,screenWidth , screenHeight)];
    self.contentView.backgroundColor = [UIColor blackColor];
    [self.view addSubview:self.contentView];
    
    
    UIBezierPath * bezierPath = [UIBezierPath bezierPath];
    [bezierPath moveToPoint:CGPointMake(0, 150)];
    [bezierPath addCurveToPoint:CGPointMake(300, 150) controlPoint1:CGPointMake(300.0*1/4, 0) controlPoint2:CGPointMake(300.0*3/4, 300)];
    
    CAShapeLayer * shapeLayer = [CAShapeLayer layer];
    shapeLayer.path = bezierPath.CGPath ;
    shapeLayer.lineWidth = 3.0 ;
//    shapeLayer.backgroundColor = [UIColor grayColor].CGColor ;
    shapeLayer.fillColor = [UIColor blueColor].CGColor ;
    shapeLayer.strokeColor = [UIColor redColor].CGColor ;
    [self.contentView.layer addSublayer:shapeLayer];
    
    // 添加一个滑动的图层
    CALayer * slipLayer = [CALayer layer];
    slipLayer.frame = CGRectMake(0, 0, 50, 50);
    slipLayer.position = CGPointMake(0, 150);
    slipLayer.contents = (__bridge id)[UIImage imageNamed:@"icon_tmpImageName"].CGImage ;
    slipLayer.affineTransform = CGAffineTransformRotate(CGAffineTransformIdentity, M_PI_4);
    [self.contentView.layer addSublayer:slipLayer];
    self.slipLayer = slipLayer;
    
    // 1.方法一
    // 根据position创建一个(图层的中心点)关建帧动画,
    CAKeyframeAnimation * keyframeAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    keyframeAnimation.duration = 4.0 ;
    keyframeAnimation.path = bezierPath.CGPath ;
    keyframeAnimation.repeatCount = 100 ;
    // 把rotationMode设置=kCAAnimationRotateAuto 根据曲线的切线自动旋转
    keyframeAnimation.rotationMode = kCAAnimationRotateAuto ;
    // slipLayer 添加一个动画
    [slipLayer addAnimation:keyframeAnimation forKey:nil];
    
    
    // 知识点:做手动动画,三角形图随着手势移动页移动
    slipLayer.speed = 0.0 ;
    [self.contentView addGestureRecognizer:[[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)]];
}

// 知识点:三角形图随着手势移动页移动
-(void)pan:(UIPanGestureRecognizer *)pan
{
    CGFloat x = [self.contentView convertPoint:[pan translationInView:self.contentView] fromView:self.view].x;
    
    NSLog(@"%f %ld",x,pan.state);
    
    if (pan.state == UIGestureRecognizerStateBegan) {
        self.startX = self.slipLayer.timeOffset / 4.0 ;
    }
    
    x = x/300.0 + self.startX;
    if (x > 1.0) {
        x = x - 1.0 ;
    }
    else if (x < 0) {
        x = 1.0 + x  ;
    }
    
    self.slipLayer.timeOffset = x*4 ;
    
}




五、UIView 动画

1)transform 属性及功能介绍

       CGAffineTransform类型,用于在二维空间做旋转、缩放和平移。(默认的值为CGAffineTransformIdentity)

1.1、简单变换

CGAffineTransformMakeRotation(CGFloat angle);      // 把view旋转angle角度。

CGAffineTransformMakeScale(CGFloat sx, CGFloat sy);     // 把view在x方向和y方向缩放的倍数。

CGAffineTransformMakeTranslation(CGFloat tx, CGFloat ty)     // 把view在x和y坐标分别平移tx和ty大小。

1.2、 混合变换 (即做缩放又要旋转的变换)

CGAffineTransformRotate(CGAffineTransform t, CGFloat angle);      // 在原来的变换基础上 把view旋转angle角度。

CGAffineTransformScale(CGAffineTransform t, CGFloat sx, CGFloat sy);      // 在原来的变换基础上 把view在x方向和y方向缩放的倍数。

CGAffineTransformTranslate(CGAffineTransform t, CGFloat tx, CGFloat ty);      // 在原来的变换基础上 把view在x和y坐标分别平移tx和ty大小。

CGAffineTransformConcat(CGAffineTransform t1, CGAffineTransform t2);      // 在两个变换的基础上创建一个新的变换值。


2) 动画

       UIView有两种方式来作动画,但这两种表面不同,实际上内部都是调用事务 CATransaction的 +begin和+commit方法做的:

@ +beginAnimations:context: 和 +commitAnimations

@ +animateWithDuration:animations:这样可以避免开发者失误造成的风险


1、过渡动画

UIView:
+transitionFromView:toView:duration:options:completion:
+transitionWithView:duration:opt ions:animations:
方法提供了Core Animation的过渡特性。但是这里的可用的过渡先项和CATransition的type属性提供的常量完全不同。UIView过渡方法中options参数可以同如下常量指定:
UIViewAnimationOptionTransitionFlipFromLeft
UIViewAnimationOptionTransitionFlipFromRight 
UIViewAnimationOptionTransitionCurlUp 
UIViewAnimationOptionTransitionCurlDown 
UIViewAnimationOptionTransitionCrossDissolve 
UIViewAnimationOptionTransitionFlipFromTop 
UIViewAnimationOptionTransitionFlipFromBottom
除了UIViewAnimationOptionTransitionCrossDissolve之外,剩下的值和CATransition类型完全没有关系。

效果:从左向右翻转,当转到180度时别一张图接着从180垂直于平面转到360度,恢复平面,图片也已经换成另一张了。
#import "ViewController.h"

@interface ViewController ()

@property (nonatomic , strong) UIImageView * imageView ;
@property (nonatomic , strong) NSArray * images ;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    self.imageView = [[UIImageView alloc] init];
    self.imageView.frame = CGRectMake(100, 100, 100, 100);
    self.imageView.userInteractionEnabled = YES ;
    [self.imageView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(switchImage)]];
    [self.view addSubview:self.imageView];
    
    self.images = @[[UIImage imageNamed:@"icon_xl"],
                    [UIImage imageNamed:@"nav_more"],
                    [UIImage imageNamed:@"slider_table"],
                    ];
    self.imageView.image = self.images[0];

}

-(void)switchImage
{
    // 1.用CATransition做过渡动画
//    CATransition * transition = [CATransition animation];
//    transition.duration = 1.0;
//    transition.type = kCATransitionFade ;
//    transition.subtype = kCATransitionFromLeft ;
//    [self.imageView.layer addAnimation:transition forKey:nil];
//    
//    // cycle to next image
//    UIImage * currentImage = self.imageView.image ;
//    NSInteger index = [self.images indexOfObject:currentImage];
//    index = (index + 1)%[self.images count];
//    self.imageView.image = self.images[index];
    
    
    // 2.用UIView方法做过渡动画
    [UIView transitionWithView:self.imageView duration:2.0 options:UIViewAnimationOptionTransitionFlipFromLeft animations:^{
        // cycle to next image
        UIImage * currentImage = self.imageView.image ;
        NSInteger index = [self.images indexOfObject:currentImage];
        index = (index + 1)%[self.images count];
        self.imageView.image = self.images[index];
    } completion:nil];
}

2、 通过layer 截图 自定义过渡动画

效果:截取的图转90度,同时缩放到原来大小的0.001倍 并 透明变成 0
#import "ViewController.h"

@interface ViewController ()

@property (nonatomic , strong) UIButton * transitionBtn ;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    self.view.backgroundColor = [UIColor redColor];
    // 添加按钮
    self.transitionBtn = [UIButton buttonWithType:UIButtonTypeCustom];
    self.transitionBtn.backgroundColor = [UIColor whiteColor];
    [self.transitionBtn setTitle:@"perform transition" forState:UIControlStateNormal];
    [self.transitionBtn addTarget:self action:@selector(performTransition) forControlEvents:UIControlEventTouchUpInside];
    self.transitionBtn.bounds = CGRectMake(0, 0, 100, 50);
    self.transitionBtn.center = self.view.center ;
    [self.view addSubview:self.transitionBtn];
    
    
}

-(void)performTransition
{
    // 1.截图
    UIGraphicsBeginImageContextWithOptions(self.view.frame.size, YES, 0.0);
    [self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
    // 截取的图片
    UIImage * currentImage = UIGraphicsGetImageFromCurrentImageContext();

    // 在使用UIGraphicsBeginImageContextWithOptions函数一定要有对应的
    // UIGraphicsEndImageContext函数作为结尾,不然会有内存泄漏
    UIGraphicsEndImageContext() ;
    
    
    // 2.把截取的图添加到view上
    UIImageView * currentImageView = [[UIImageView alloc] initWithImage:currentImage];
    currentImageView.frame = self.view.bounds ;
    [self.view addSubview:currentImageView];
    
    // 3.随机改变view的背景色
    CGFloat red = arc4random() / (CGFloat)INT_MAX ;
    CGFloat greed = arc4random() / (CGFloat)INT_MAX ;
    CGFloat blue = arc4random() / (CGFloat)INT_MAX ;
    self.view.backgroundColor = [UIColor colorWithRed:red green:greed blue:blue alpha:1.0];
    
    // 4.设置动画
    [UIView animateWithDuration:1.5 animations:^{
        
        CGAffineTransform transform = CGAffineTransformMakeScale(0.0001f, 0.0001f);
        transform = CGAffineTransformRotate(transform, M_PI_2) ;
        currentImageView.transform = transform ;
        currentImageView.alpha = 0.0 ;
        
    } completion:^(BOOL finished) {
        [currentImageView removeFromSuperview];
    }];

}


3、缓冲动画,设置options传的参数 :

UIKit的动画也支持这些缓冲方法,只是语法和常量有些不同。

为了改变UIView动画的缓冲选项,给options参数添加如下常量之一:

UIViewAnimationOptionCurveEaseInOut  // 默认值

UIViewAnimationOptionCurveEaseIn 

UIViewAnimationOptionCurveEaseOut 

UIViewAnimationOptionCurveLinear


和CAMediaTimingFunction差不多,只是没有kCAMediaTimingFunctionDefault对应的值了。






六、Run Loop 模式
1)三种模式

NSDefaultRunLoopMode        - 标准优先级

NSRunLoopCommonModes  - 高级优先级

UITrackingRunLoopMode      - 用于UIScrollView与别的控件的动画




  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值