• iOS:动画(18-10-15更)


    目录

      1、UIView Animation

        1-1、UIView Animation(基本使用)

        1-2、UIView Animation(转场动画)

      2、CATransaction(Layer版的UIView Animation)

      3、CAAnimation

        3-0、CALayer移除、取Animation。

        3-1、- CAAnimation(base基类)

        3-2、  | - CAPropertyAnimation(属性动画)

        3-3、  |  | - CABasicAnimation(基础动画)

        3-4、  |  |  | - CASpringAnimation(弹簧动画 iOS 9.0+!

        3-5、  |  | - CAKeyframeAnimation(关键帧、路径动画)

        3-6、  | - CATransition(转场动画)

        3-7、  | - CAAnimationGroup(动画组)

        3-附录、旋转 >= 180 度

      ...

      N-1、UIDynamicAnimator(物理引擎)

      N、粒子动画

    其他

      1、模态视图转场动画

      2、自定义转场动画(相当于快照当前屏幕)

      3、动画所需的变换  

        3-1、view:CGAffineTransform(缩放、位移、旋转。二维变换)

        3-2、layer:CATransform3D(缩放、位移、旋转。三维变换)

      4、设置缺省动画(隐式动画)

    0、写在前面

      1)、当view做变换后,frame的宽高为所需最小的(外切)矩形,不再是原来尺寸,原来的可用bounds获取。

          如4:3的宽高旋转90度,frame的宽高就变成3:4。bounds,还是原来的4:3。

      2)、view.layer.anchorPoint ,影响动画效果

      3)、旋转,>=180度,会反方向旋转(自动寻找最短路径?),解决方案,参照 “3、CAAnimation” -> “3-附录、旋转 >= 180 度”

      4)、UIImageView 有自带的动画属性、方法:

    // 本地动画图片帧 添加到 数组
    for (int i=0; i<7; i++)
    {
    	NSString *imgName = [NSString stringWithFormat:@“images_%d”,i+1];
    	UIImage *image = [UIImage imageNamed:imgName];
    	[arr  addObject:image];
    }
    
    // 动画图片帧的数组,动画时间,动画次数
    animationImageView.animationImages = arr;
    animationImageView.animationDuration = time;
    animationImageView.animationRepeatCount = count;
    [animationImageView startAnimating];
    

         补充:1、无法获得结束的代理,要么监听 animating 属性,要么用 CAKeyframeAnimation 动画替换。

           2、设置的 animationDuration 不精确,如果用延时等待结束,再操作该imageView,记得要先停止动画 stopAnimating,否则,有时候正常,有时候修改图片,又被动画覆盖掉,差几毫秒的可能。

    1、UIView Animation

      1-1、UIView Animation(基本使用)

        1)、原始的,非Block。

    //动画名、内容
    [UIView beginAnimations:@"id100" context:@"animation1"];
    //时长2秒
    [UIView setAnimationDuration:2.0];	
    //动画速度
    //    UIViewAnimationCurveEaseInOut,  // 慢入、慢出
    //    UIViewAnimationCurveEaseIn,     // 慢入
    //    UIViewAnimationCurveEaseOut,    // 慢出
    //    UIViewAnimationCurveLinear,     // 匀速
    [UIView setAnimationCurve:UIViewAnimationCurveEaseIn];	
    //设置代理
    [UIView setAnimationDelegate:self];
    //动画将要开始SEL
    [UIView setAnimationWillStartSelector:@selector(animationWillStart:context:)];	
    //动画将要结束SEL
    [UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)];
    //提交动画
    [UIView commitAnimations];
    
    //签代理后,才能实现SEL的方法(方法名随意,参数个数也随意,分别少于2/3个的时候,只收到前面几个参数,多于它的参数,后面参数空,有用过C的回调函数,应该会比较熟悉)
    //开始动画时的方法
    -(void)animationWillStart:(NSString *)animationID context:(void *)context
    {
        NSLog(@"动画开始");
    }
    //结束动画时的方法
    -(void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context
    {
        //可以判断ID,做连续动画
        NSLog(@"动画结束");
    }
    //自己取名
    -(void)theAnimationStop:(NSString *)animationID thecontext:(void *)theContext context:(void *)context2
    {
    NSLog(@"%@,%@,%@",animationID,theContext,context2);
    }    
    

        2)、Block

    // 基础
    [UIView animateWithDuration:1.0 animations:^{
        // 新的值
    }];
    
    // 嵌套,做连续动画
    [UIView animateWithDuration:2.0 animations:^{
        self.cyanVIew.frame = CGRectMake(100, 400, 100, 100);
    } completion:^(BOOL finished) {
        [UIView animateWithDuration:2.0 animations:^{
            self.cyanVIew.frame = CGRectMake(100, 100, 100, 100);
        }];
    }];
    
    // 可以设置一些 延时、动画速度 可选项等。
    [UIView animateWithDuration:2.0
                          delay:0.0
                        options:UIViewAnimationOptionCurveEaseInOut
                     animations:^{
                         // 新的值
                     } completion:^(BOOL finished) {
                         // 动画完成,可以再嵌套动画
                     }];
    
    // 弹簧/缓冲 动画(7.0以后)
    [UIView animateWithDuration:2.0
                          delay:0.0
         usingSpringWithDamping:0.5     // 震荡周期(0-1)
          initialSpringVelocity:5.0     // 初始速度
                        options:UIViewAnimationOptionCurveEaseIn
                     animations:^{
                         // 新的值
                     } completion:^(BOOL finished) {
                         // 动画完成,可以再嵌套动画
                     }];
    
    // 不懂(7.0以后)
    [UIView animateKeyframesWithDuration:1.0
                                   delay:0.0
                                 options:UIViewKeyframeAnimationOptionCalculationModeCubic
                              animations:^{
                                  // 新的值
                              } completion:^(BOOL finished) {
                                  // 动画完成,可以再嵌套动画
                              }];
    

     

      1-2、UIView Animation(转场动画)

         1)、UIView Animation 方法

    // 参数cache,
    // YES为截图后再转场,减轻系统负担,动画更流畅,
    // NO为一起动画,如需要边转场边动画的效果
    
    //     UIViewAnimationTransitionNone,
    //     UIViewAnimationTransitionFlipFromLeft,     //左边下翻效果(X信,好友历史说说,查看详情)
    //     UIViewAnimationTransitionFlipFromRight,   //右边下翻效果
    //     UIViewAnimationTransitionCurlUp,               //上翻日历效果
    //     UIViewAnimationTransitionCurlDown,           //下盖日历效果
    
    //1、导航栏转场
    [UIView animateWithDuration:2.0 animations:^{
        [UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:self.navigationController.view cache:YES];
        [self.navigationController pushViewController:vc animated:NO];
    }];
    
    //2、普通View转场,把当前View放在最底层,最好大小全相同,不然动画效果很尴尬
    [UIView animateWithDuration:2.0 animations:^{
        //转场动画
        [UIView setAnimationTransition:UIViewAnimationTransitionCurlDown forView:bgView cache:YES];
        //最上面的View放到最下面
        exchangeSubviewAtIndex:withSubviewAtIndex:
        [bgView sendSubviewToBack:[[bgView subviews] lastObject]];
    }];
    

        2)、UIView transition 方法  

        // 1、以当前View的范围,对子fronView、子backView做动画
        [UIView transitionFromView:self.fronView
                            toView:self.backView
                          duration:1.0
                           options:UIViewAnimationOptionTransitionCurlUp
                        completion:^(BOOL finished) {
    
                        }];
        
        // 2、以View的范围做动画
        [UIView transitionWithView:self.imageView
                          duration:1.0
                           options:UIViewAnimationOptionTransitionCurlUp
                        animations:^{
                            
                            UIImage *currentImage = self.imageView.image;
                            NSUInteger index = [self.images indexOfObject:currentImage];
                            index = (index + 1) % [self.images count];
                            self.imageView.image = self.images[index];
                            
                        } completion:^(BOOL finished) {
    
                        }];
    

      

    2、CATransaction(Layer版的UIView Animation) 

        // 开始
        [CATransaction begin];
        // 时长,默认0.25秒
        [CATransaction setAnimationDuration:1.0];
        // 慢入、快出等。
        [CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];
        // 完成 或 删除 后的block
        [CATransaction setCompletionBlock:^{
            
        }];
        // 提交
        [CATransaction commit];
        
        // =============== 未知 ===============
        [CATransaction flush];
        [CATransaction lock];
        [CATransaction unlock];
    
        // =============== 禁止layer自带的动画 ===============
        [CATransaction begin];
        [CATransaction setDisableActions:YES];
        // 改变Layer的属性
        [CATransaction commit];
    

    3、CAAnimation

      3-0、CALayer移除、取Animation。

        [self.testLayer animationForKey:@"test_animation"];
        [self.testLayer animationKeys];
        
        [self.testLayer removeAnimationForKey:@"test_animation"];
        [self.testLayer removeAllAnimations];
    

      3-1、CAAnimation

        1)、主要属性:removedOnCompletion 完成移除、timingFunction 慢入快出等、delegate 代理 。 

    // 完成动画自动移除。默认YES。NO需要手动移除,否则会占用内存
    animation.removedOnCompletion = NO;
    
    //  动画速度(慢入、慢出)
    //    kCAMediaTimingFunctionLinear        //匀速
    //    kCAMediaTimingFunctionEaseIn        //慢入
    //    kCAMediaTimingFunctionEaseOut       //慢出
    //    kCAMediaTimingFunctionEaseInEaseOut //慢入慢出
    //    kCAMediaTimingFunctionDefault       //匀速,不过有点快
    animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
    
    // 代理
    animation.delegate = self;
    
    - (void)animationDidStart:(CAAnimation *)anim{
    }
    // flag = YES 完成动画,flag = NO animation没做完被remove了
    - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
    }
    

    补充:如同一个VC、View 多个动画,代理也无法分清。

       因为,代理的参数传进来是深拷贝过的,就算比对self.CAAnimation和代理的anim,地址不同,也会比对失败。

       但是,CAAnimation本身类似一个字典,所以,可以随意设值,来匹配。

    // 标注哪个Layer的动画
    [animation setValue:testLayer forKey:@"handLayer"];
    
    - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
    {
        CALayer *testLayer = [anim valueForKey:@"handLayer"];
        CABasicAnimation *baseAnim = (CABasicAnimation*)anim;
        
        // 需要禁掉layer自带的属性变化动画,View没有。
        // 不禁的话,变化完,会跳回原来的,再做自带的动画变化。
        [CATransaction begin];
        [CATransaction setDisableActions:YES];
        testLayer.transform = [baseAnim.toValue CATransform3DValue];
        [CATransaction commit];
    }

        2)、主要协议<CAMediaTiming>:duration 每次动画时间、repeatCount 重复次数等。重复次数、重复时长二选一。

        // 动画延迟开始时间
        animation.beginTime = CACurrentMediaTime() + 2.0;
        // 动画周期(时间)
        animation.duration = 3.0;
        // 动画速度(快进、慢放)
        animation.speed = 2.0;
        // 动画起始偏移(偏移量的位置比例为timeOffset / duration,与speed无关。如果播放次数为1次,那么动画就从偏移量开始,播放到尾 - 头 - 偏移量)
        animation.timeOffset = 1.0;
        // 动画重复次数(不包含倒放的次数)
        animation.repeatCount = INFINITY;
        // 动画重复时间(包含倒放的时间)
        animation.repeatDuration = INFINITY;
        // 动画播完倒放到原来的状态(时长一样)
        animation.autoreverses = YES;
    
        // 动画更新属性时间:动画 前、后、前后、移除
    //    kCAFillModeForwards     // 开始:无动作,等待beginTime到达。                结束:保持结束位置。
    //    kCAFillModeBackwards    // 开始:迅速到达动画开始位置、状态,等待beginTime到达。结束:返回动画前位置。
    //    kCAFillModeBoth         // 开始:迅速到达动画开始位置、状态,等待beginTime到达。结束:保持结束位置。
    //    kCAFillModeRemoved      // 开始:无动作,等待beginTime到达。                结束:返回动画前位置。
        animation.fillMode = kCAFillModeBoth;
    

       补充:1、动画添加上去,就不能修改了,所以如果要暂停动画,快近等,需要对图层设置。

         2、不会影响到子图层:duration、repeatCount、repeatDuration。

         3、会影响到子图层 :beginTime、timeOffset、speed。

         4、当 layer.speed = 0,不断改变timeOffset,可以手动动画。

         5、不同图层时间转换

            - (CFTimeInterval)convertTime:(CFTimeInterval)t fromLayer:(CALayer *)l;

            - (CFTimeInterval)convertTime:(CFTimeInterval)t toLayer:(CALayer *)l;

      3-2、CAPropertyAnimation

        1)、主要属性:keyPath 变换的属性。

        // 属性
        animation.keyPath = @"transform";
    

      3-3、CABasicAnimation(基础动画)

        1)、例子。CABasicAnimation 只有 fromValue、byValue、toValue 三个属性,其他是继承。

        CABasicAnimation *animation = [CABasicAnimation animation];
        // 动画的属性
        animation.keyPath = @"transform";
        // 初始值:不设置、nil 默认为 当前值
        animation.fromValue = [NSValue valueWithCATransform3D:fromTransform];
        // 相对值:相对当前的增量
        animation.byValue = [NSValue valueWithCATransform3D:byTransform];
        // 绝对值:变成什么样
        animation.toValue = [NSValue valueWithCATransform3D:toTransform];
        // 动画时间
        animation.duration = 0.5;
        // 添加动画
        [self.testLayer addAnimation:animation forKey:@"test_transform"];
    

      补充:开始动画,会突然跳到开始的Value。

         结束动画,会突然跳到最开始的Value。

         所以需要在开始、结束手动调整到变化的属性,不然很突兀。

         参照 “2-1)、CAAnimation” ->  “1)、主要属性:delegate 代理” -> “补充”。

      3-4、CASpringAnimation(弹簧动画 iOS 9.0+!

        CASpringAnimation *animation = [CASpringAnimation animation];
        // 质量 > 0 ,默认 1(值越大,震荡范围越大、越久)
        animation.mass = 1;
        // 弹簧刚度系数 > 0 ,默认 100 (值越大,震荡范围越大、越快)
        animation.stiffness = 100;
        // 阻尼系数 >= 0 ,默认 10(阻力)
        animation.damping = 10;
        // 初始速度
        animation.initialVelocity = 5.0;
        // 动画时间
        animation.duration = animation.settlingDuration;
    
        // 继承CABaseAnimation
        animation.keyPath = @"position";
        animation.fromValue = [NSValue valueWithCGPoint:CGPointMake(0, 200)];
        animation.toValue = [NSValue valueWithCGPoint:CGPointMake(200, 100)];
        [self.testLayer addAnimation:animation forKey:@"xx"];
    

     

      3-5、CAKeyframeAnimation(关键帧动画)

        1)、关键帧动画,主要属性:values(帧)、keyTimes(帧对应的时间点)、calculationMode(连续、离散等)。

        CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
        animation.keyPath = @"position";
        animation.duration = 10.0;
        animation.values = @[
                             [NSValue valueWithCGPoint:CGPointMake(50, 50)],
                             [NSValue valueWithCGPoint:CGPointMake(100, 180)],
                             [NSValue valueWithCGPoint:CGPointMake(150, 120)],
                             [NSValue valueWithCGPoint:CGPointMake(50,50)]
                             ];
        animation.keyTimes = @[@0.0,@0.3,@0.8,@1.0];
        
        // 动画速度(默认控制全局,慢入慢出)
        // 1、控制每个关键帧的切换。如每次切换 慢入快出 ,像呼吸灯。个数少1。
    //    CAMediaTimingFunction *fn = [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseIn];
    //    animation.timingFunctions = @[fn, fn, fn];
        // 2、控制整体动画。如用贝塞尔曲线,决定加速度。
    //    animation.timingFunction = [CAMediaTimingFunction functionWithControlPoints:1 :0 :0.75 :1];
    
        // 默认线性
        // kCAAnimationLinear
        // 离散
        // kCAAnimationDiscrete
        // kCAAnimationPaced
        // kCAAnimationCubic
        // kCAAnimationCubicPaced
        animation.calculationMode = kCAAnimationLinear;
        
        [self.testLayer addAnimation:animation forKey:nil];
    

      补充:同CABasicAnimation,如果变化初值、结束值不同原来的,会跳变。

         初值:最好相同。

         结束值:1、要么相同值。2、要么变化完修改属性。

       再补充:可以通过下面方法,获取系统的 慢入慢出等动画,加速度的贝塞尔曲线控制点。

        CAMediaTimingFunction *function = [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseOut];
    
        CGPoint controlPoint1, controlPoint2;
        [function getControlPointAtIndex:1 values:(float *)&controlPoint1];
        [function getControlPointAtIndex:2 values:(float *)&controlPoint2];
    

      

        2)、路径动画。主要属性:path 路径、rotationMode 设置后layer跟着旋转。

    #if 0
        //创建可变路径
        CGMutablePathRef path = CGPathCreateMutable();
        //变换
        CGAffineTransform transform = CGAffineTransformMakeTranslation(0, 200);
        //椭圆(变换可空)
        CGPathAddEllipseInRect(path, &transform,  CGRectMake(50, 100, 300, 300));
    #endif
        // 路径
        UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(50, 100, 300, 300)];
    
        CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
        animation.keyPath = @"position";
        animation.duration = 10.0;
        // 路径
        animation.path = path.CGPath;
        // layer的内容跟着旋转
        animation.rotationMode = kCAAnimationRotateAutoReverse;
        
        [self.testLayer addAnimation:animation forKey:nil];
    

      补充:如要画轨迹,用同一个path配合 CAShapeLayer ,参照《iOS:绘图》 -> “1、UIBezierPath(贝塞尔曲线)” -> “2)、”

      3-6、CATransition(转场动画)

        1)、动画设置。主要属性:type 动画效果、subtype 动画方向。

        CATransition *animation = [CATransition animation];
        animation.duration = 1.5;
        //动画类型
        //    kCATransitionFade           //淡入
        //    kCATransitionMoveIn         //覆盖
        //    kCATransitionPush           //推
        //    kCATransitionReveal         //掀起,相对覆盖
        //以下为私有API
        //    @"cube"                     //立方体(某宝的AR切换)
        //    @"suckEffect"               //吮吸
        //    @"oglFlip"                  //翻转(某信,好友历史说说,查看详情)
        //    @"rippleEffect"             //波纹
        //    @"pageCurl"                 //日历上翻
        //    @"pageUnCurl"               //日历下盖
        //    @"cameraIrisHollowOpen"     //相机打开
        //    @"cameraIrisHollowClose"    //相机关闭
        animation.type = @"cube";
        //动画方向
        //    kCATransitionFromRight      //从右边
        //    kCATransitionFromLeft       //从左边
        //    kCATransitionFromTop        //从上面
        //    kCATransitionFromBottom     //从下面
        animation.subtype = kCATransitionFromLeft;
        // 从动画的0.0开始
    //    animation.startProgress = 0.0;
        // 到动画的1.0结束
    //    animation.endProgress = 1.0;
    

        2)、添加

          2-1)、普通View的切换动画

    // 添加动画到普通的View
    [bgView.layer addAnimation:animation forKey:@"view_transition"];
    // 把最上面的View放到最底层,一般转场可能就2层相互转换,也可用
    exchangeSubviewAtIndex:withSubviewAtIndex:
    [bgView sendSubviewToBack:[[bgView subviews] lastObject]];
    

          2-2)、导航栏动画

    // 添加动画到导航栏
    [self.navigationController.view.layer addAnimation:animation forKey:@"navi_transition"];
    [self.navigationController pushViewController:vc animated:NO];

        3)、tabbar动画

    // 设置代理
    tbVC.delegate = self;
    
    - (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController
    {
        // 当前的VC Index
        NSLog(@"--%ld",tabBarController.selectedIndex);
        return YES;
    }
    
    - (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
    {
        // 点中的VC Index
        NSLog(@"==%ld",tabBarController.selectedIndex);
        
        // 每次切换都加动画
        CATransition *animation = [CATransition animation];
        animation.type = kCATransitionPush;
        // 判断从左还是从右切换
        if (/*当前VC的Index和点中的VC的Index比较*/) {
            animation.subtype = kCATransitionFromLeft;
        }else{
            animation.subtype = kCATransitionFromRight;
        }
        // 添加动画
        [tbVC.view.layer addAnimation:animation forKey:@"tabbar_transition"];
    }
    

     

      3-7、CAAnimationGroup(动画组)

         1)、只有一个属性:animations 动画组。例子效果:边运动边变换颜色。

        // 路径动画
        UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(50, 100, 300, 300)];
        CAKeyframeAnimation *animation1 = [CAKeyframeAnimation animation];
        animation1.keyPath = @"position";
        animation1.path = path.CGPath;
    
        // 关键帧动画
        CAKeyframeAnimation *animation2 = [CAKeyframeAnimation animation];
        animation2.keyPath = @"backgroundColor";
        animation2.values = @[
                             (__bridge id)[UIColor blueColor].CGColor,
                             (__bridge id)[UIColor orangeColor].CGColor,
                             (__bridge id)[UIColor greenColor].CGColor,
                             (__bridge id)[UIColor blueColor].CGColor
                             ];
        // ===========================================================================================
        // 动画组
        CAAnimationGroup *group = [CAAnimationGroup animation];
        group.duration = 10.0;
        group.animations = @[animation1,animation2];
        [self.testLayer addAnimation:group forKey:@"test_group"];
    

      3-附录、旋转 >= 180 度

        1)、CAKeyframeAnimation

        CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
        animation.keyPath = @"transform";
        animation.duration = 2.0;
        animation.values = @[
                             [NSValue valueWithCATransform3D:CATransform3DMakeRotation(0, 0, 0, 1)],
                             [NSValue valueWithCATransform3D:CATransform3DMakeRotation(M_PI_2, 0, 0, 1)],
                             [NSValue valueWithCATransform3D:CATransform3DMakeRotation(M_PI, 0, 0, 1)],
                             [NSValue valueWithCATransform3D:CATransform3DMakeRotation(3*M_PI_2, 0, 0, 1)],
                             [NSValue valueWithCATransform3D:CATransform3DMakeRotation(2*M_PI, 0, 0, 1)],
                             ];
    
        animation.calculationMode = kCAAnimationLinear;
         [self.testLayer addAnimation:animation forKey:nil];
    

        2)、CABasicAnimation(虚拟属性transform.rotation)

        CABasicAnimation *animation = [CABasicAnimation animation];
        animation.keyPath = @"transform.rotation";
        animation.duration = 2.0;
        animation.byValue = @(M_PI * 2);
        [self.testLayer addAnimation:animation forKey:@"test_transform.rotation"];
    

    N-1、UIDynamicAnimator(物理引擎)

    写在前面:

      行为都继承UIDynamicBehavior,所以,有运动中Block

        behavior.action = ^{
    
        };
    

      物理引擎添加各种行为

        //动画作用的局域
        UIDynamicAnimator *animator = [[UIDynamicAnimator alloc] initWithReferenceView:self];
        
        ...
        
        //添加行为
        [animator addBehavior:behavior];
    

      1)、UIGravityBehavior(重力行为)

        //重力行为(只创建不设置属性也可以,默认角度M_PI_2、加速度量级为1的重力)
        UIGravityBehavior *gravity = [[UIGravityBehavior alloc]initWithItems:@[cyanView]];
        
        //重力角度
        //    gravity.angle = M_PI_4;
        //重力量级(加速度)
        //    gravity.magnitude = 0.1;
        //重力角度 和 重力量级(加速度)
        //    [gravity setAngle:M_PI_4 magnitude:0.1];
        
        //重力矢量 0.0 - 1.0
        //设置后,上面的角度、加速度量级都失效,如果矢量超过1加速度也会变大。
        gravity.gravityDirection = CGVectorMake(1, 1);
    

      2)、UIPushBehavior(推力行为)

    //    UIPushBehaviorModeContinuous,       //持续作用力
    //    UIPushBehaviorModeInstantaneous     //推一把
        UIPushBehavior *push = [[UIPushBehavior alloc]initWithItems:@[redLabel] mode:UIPushBehaviorModeInstantaneous];
        
        //推力角度
    //    push.angle = M_PI_4;
        //推力量级
    //    push.magnitude = 1;
        //推力角度 和 推力量级
    //    [push setAngle:M_PI_4 magnitude:0.1];
    
        //推力矢量 0.0 - 1.0
        //设置后,上面的角度、量级都失效,如果矢量超过1速度也会变大。
        push.pushDirection = CGVectorMake(1, 1);
        
        
        //设置该行为下的item 推力的偏离量,效果如打桌球的边角,或是滚动的球
        [push setTargetOffsetFromCenter:UIOffsetMake(15, 0) forItem:redLabel];
    
        [animator addBehavior:push];
    

      3)、UICollisionBehavior(碰撞行为)

        //碰撞行为
        UICollisionBehavior *collision = [[UICollisionBehavior alloc] initWithItems:@[cyanView,redLabel]];
        
        //获取 相互碰撞的items 、 items相互碰撞点 、 碰撞的边界ID
        //    collision.collisionDelegate = self;
        
        //碰撞方式
        //    UICollisionBehaviorModeItems        //items相互碰撞
        //    UICollisionBehaviorModeBoundaries   //items只与边界碰撞
        //    UICollisionBehaviorModeEverything   //items、边界都碰撞
        collision.collisionMode = UICollisionBehaviorModeEverything;
        
        //碰撞边界
        //1、转换物理引擎UIDynamicAnimator的Reference边界成碰撞的边界
        collision.translatesReferenceBoundsIntoBoundary = YES;
        
        //2、转换物理引擎UIDynamicAnimator的Reference边界成碰撞的边界,且设置有内边距
        //    [collision setTranslatesReferenceBoundsIntoBoundaryWithInsets:UIEdgeInsetsMake(100, 60, 50, 60)];
        
        //3、设置边界为一个屏幕的内切圆路径
        //    [collision addBoundaryWithIdentifier:@"test" forPath:[UIBezierPath bezierPathWithOvalInRect:SCREEN_BOUNDS]];
        
        //4、设置边界为为一条直线。想要一个封闭的空间,至少要添加3次,即三角形
        //    [collision addBoundaryWithIdentifier:@"test" fromPoint:CGPointMake(50, 500) toPoint:CGPointMake(300, 500)];
    

      4)、UISnapBehavior(迅速移动行为)

        //创建迅速移动行为
        UISnapBehavior *snap = [[UISnapBehavior alloc] initWithItem:cyanView snapToPoint:point];
        //更新移动点
        snap.snapPoint = CGPointMake(50, 50);
        //值越小 抖动越厉害 0.0 - 1.0
        snap.damping = 0.5;
    

      5)、UIAttachmentBehavior(吸附行为)

        1)、旧版功能,已测试,没问题。

          1、如果同时加重力,就想摆钟一样,吸附距离就像一根绳子(类似划断绳子,礼物下落的小游戏)。

          2、如果实时用触摸点设置锚点,就是拖到哪移动哪,同时带有震荡效果(如果不需要震荡效果,简单的transform都能实现。)

        //1、item 吸附到点 Anchor
        UIAttachmentBehavior *attachment = [[UIAttachmentBehavior alloc]initWithItem:cyanView attachedToAnchor:[touches.anyObject locationInView:self]];
        //2、item 吸附到点 Anchor,且item的中心点有x、y偏移
    //    UIAttachmentBehavior *attachment = [[UIAttachmentBehavior alloc]initWithItem:cyanView offsetFromCenter:UIOffsetMake(15, 15) attachedToAnchor:[touches.anyObject locationInView:self]];
        //3、item 吸附到 item,同时向对方移动
    //    UIAttachmentBehavior *attachment = [[UIAttachmentBehavior alloc]initWithItem:cyanView attachedToItem:redLabel];
        //4、item 吸附到 item,同时向对方移动,且有x、y偏移
    //    UIAttachmentBehavior *attachment = [[UIAttachmentBehavior alloc]initWithItem:cyanView offsetFromCenter:UIOffsetMake(0, 0) attachedToItem:redLabel offsetFromCenter:UIOffsetMake(0, 0)];
    
        //长度必须设置。item吸附过来时候,item的center 距离 锚点 距离 / 两个item的center的距离 / 两个item的center,再加偏移的距离
        attachment.length = 100;
        //阻尼
        attachment.damping = 200;
        //震荡频率
        attachment.frequency = 50;
    

        2)、iOS9后添加的新方法,什么鬼效果都不知道

        //5、拖动item。属性attachmentRange为item距离另一个item达到临界才开始拖动。(锚点、方向向量参数没用???)
        UIAttachmentBehavior *attachment = [UIAttachmentBehavior slidingAttachmentWithItem:redLabel attachedToItem:cyanView attachmentAnchor:[touches.anyObject locationInView:self] axisOfTranslation:CGVectorMake(0, 1)];
        //6、拖动item。属性attachmentRange为拖动距离。配合push,只能按固定的方向移动,push方向不对,将不会动(锚点参数没用???)
    //    UIAttachmentBehavior *attachment = [UIAttachmentBehavior slidingAttachmentWithItem:cyanView attachmentAnchor:[touches.anyObject locationInView:self] axisOfTranslation:CGVectorMake(1, 1)];
        //7、两个item之间按frame的距离固定在一起,另一个item会被拖着动,不是整体固定。默认锚点为center,可设置偏移offset(有偏移时,会转来转去,且距离会变长)
    //    UIAttachmentBehavior *attachment = [UIAttachmentBehavior limitAttachmentWithItem:cyanView offsetFromCenter:UIOffsetMake(0, 0) attachedToItem:redLabel offsetFromCenter:UIOffsetMake(0, 0)];
        //8、两个item之间按frame的距离固定在一起。要动一起旋转动(锚点参数没用???)
    //    UIAttachmentBehavior *attachment = [UIAttachmentBehavior fixedAttachmentWithItem:cyanView attachedToItem:redLabel attachmentAnchor:[touches.anyObject locationInView:self]];
        //9、设置一个锚点。item的center到锚点的距离 + 锚点到item2的center距离,等于两个item的固定距离,相互牵引。
    //    UIAttachmentBehavior *attachment = [UIAttachmentBehavior pinAttachmentWithItem:cyanView attachedToItem:redLabel attachmentAnchor:[touches.anyObject locationInView:self]];
    
        //摩擦力矩(???)
        attachment.frictionTorque  = 500;
        //吸附范围,当两个item超出200的距离时,item就被另一item拖动
        attachment.attachmentRange = UIFloatRangeMake(-50, 50);
    

      6)、UIDynamicItemBehavior(动力元素行为)

        // 动力元素行为
        UIDynamicItemBehavior * itemBehavior = [[UIDynamicItemBehavior alloc] initWithItems:@[redLabel]];
        //弹性(0.0-1.0:非弹性-弹性)
        itemBehavior.elasticity = 1;
        //摩擦(刚好贴着运动好像没效果,要有一定角度的力,让其贴住其他item或者边界才有摩擦)
        itemBehavior.friction = 1;
        //密度(碰撞时,密度高的不怎么动)
        itemBehavior.density = 1;
        //阻力系数(阻碍移动,会阻碍重力、也会阻碍反弹)
        itemBehavior.resistance = 40;
        //角阻力(阻碍旋转)
        itemBehavior.angularResistance = 0;
        //允许旋转
        itemBehavior.allowsRotation = YES;
        //线速度(阻力 resistance 依然有效)
        [itemBehavior addLinearVelocity:CGPointMake(50, 0) forItem:redLabel];
        //角速度(添加后,允许旋转 allowsRotation = YES,无法修改。角阻力 angularResistance 依然有效)
        [itemBehavior addAngularVelocity:M_PI_4 forItem:redLabel];
    

    N、粒子动画

      0)、写在前面

        粒子发射层 继承CALayer,那么阴影、边界、圆角的属性都有。

        y轴/x轴速度 小于 加速度(重力) 会有 雪花/风力 效果(cell.velocity < cell.yAcceleration)。

        y轴速度 稍大于 加速度(重力) 会有 发射/投掷 效果(cell.velocity > cell.yAcceleration)。

        粒子发射层 设为叠加模式会有火焰效果(emitterLayer.renderMode = kCAEmitterLayerAdditive)。

        粒子的名字。可以用KVC来设置属性([emitterLayer setValue:[NSNumber numberWithFloat:M_PI] forKey:@"emitterCells.test2.emissionLongitude"];)。

        粒子缩放速度。当缩放到0的时候,又会放大,所以想要从birth到lifetime结束都是缩放形式,就需要不断调值,不要缩放太快(cell.scaleSpeed)。

        粒子包含粒子。颜色,速度之类的都会叠加,会有意想不到的效果(emitterLayer.emitterCells = @[cell]; cell.emitterCells = @[cell2];)。

        粒子的形状emitterShape、模型emitterMode 搞了很久没搞懂。多亏这边文章( http://www.cnblogs.com/densefog/p/5424155.html )

      1)、创建粒子发射层

        //创建 粒子发射器层layer
        emitterLayer = [CAEmitterLayer layer];
        //发射器 frame(可设置合适大小来 masksToBounds )
        emitterLayer.frame = self.view.bounds;
        //发射器 发射源起点
        emitterLayer.emitterPosition = CGPointMake(emitterLayer.frame.size.width / 2.0, emitterLayer.frame.size.height/ 2.0);
        //发射器 发射源size
        emitterLayer.emitterSize = CGSizeMake(100.0, 100.0);
        //混合方式(Add是叠加,有火焰效果,其他覆盖)
    //    kCAEmitterLayerUnordered    //无序
    //    kCAEmitterLayerOldestFirst  //老的在(数组)最前面(显示最下层)
    //    kCAEmitterLayerOldestLast   //老的在(数组)最后面(显示最上层)
    //    kCAEmitterLayerBackToFront  //
    //    kCAEmitterLayerAdditive     //叠加
        emitterLayer.renderMode = kCAEmitterLayerOldestLast;
        //是否开启超三维空间模式(NO慢慢变白透明消失,YES直接透明消失)
        emitterLayer.preservesDepth = YES;
        //发射器形状
    //    kCAEmitterLayerPoint        //发射源一个点,位置来源(emitterPosition)
    //    kCAEmitterLayerLine         //发射源一条线,位置来源(emitterSize,正中的横线)
    //    kCAEmitterLayerRectangle    //发射源一个矩形,位置来源(emitterSize)
    //    kCAEmitterLayerCuboid       //发射源一个立方体,位置来源(emitterSize + emitterZPosition)
    //    kCAEmitterLayerCircle       //发射源一个圆形,位置来源(emitterSize 的内切圆)
    //    kCAEmitterLayerSphere       //发射源一个立体圆形,位置来源(emitterSize + emitterZPosition的内切圆)
        emitterLayer.emitterShape = kCAEmitterLayerPoint;
        //发射模型
    //    kCAEmitterLayerPoints       //发射模型一个点
    //    kCAEmitterLayerOutline      //发射模型外框边界
    //    kCAEmitterLayerSurface      //发射模型矩阵里面
    //    kCAEmitterLayerVolume       //
        emitterLayer.emitterMode = kCAEmitterLayerPoints;
        //发射Z轴起点
        emitterLayer.emitterZPosition = 50.0;
        //发射器的深度
        emitterLayer.emitterDepth = 50.0;
        //在一开始生成随机的粒子数?(暂时看不出效果)
        emitterLayer.seed = 80;
        //添加到layer层
        [self.view.layer addSublayer:emitterLayer];
    

      2)、创建粒子

        //创建 粒子
        CAEmitterCell *cell = [[CAEmitterCell alloc] init];
        //粒子 名称
        cell.name = @"test";
        //粒子 图片
        cell.contents = (__bridge id _Nullable)([UIImage imageNamed:@"2"].CGImage);
        //粒子 y轴加速度(重力)
        cell.yAcceleration = 75.f;//-50.f;
        //粒子 x轴加速度(重力)
        cell.xAcceleration = 0.f;
        //粒子 生成速率(/秒,>1.0)
        cell.birthRate = 1;
        //粒子 生命周期
        cell.lifetime = 5.0;
        //粒子 颜色
        cell.color = [UIColor colorWithRed:1 green:0.5 blue:0.1 alpha:1.0].CGColor;
        //粒子 透明速度(-0.0 <-> -1.0,-0.9消失得快,-0.1消失得慢)
        cell.alphaSpeed = -0.1;
        //粒子 运动的速度
        cell.velocity = 50;
        //粒子 运动的速度范围(velocity +- Range/2 )
        cell.velocityRange = 50;
        //粒子旋转角度(0 - Range)
        cell.spin = 0;
        //粒子旋转角度范围(0 - Range)
        cell.spinRange = 0;
        //粒子 缩放
        cell.scale = 1;
        //粒子 缩放速度(-1 <-> 1 ,-变小 +变大,烟花爆炸后效果,如果变小到0又会增大,值需要调试)
        cell.scaleSpeed = -0.5;
        //粒子 发射角度
        cell.emissionLongitude = (M_PI/2)*3;
        //粒子 发射角度范围(Longitude +- Range/2 )
        cell.emissionRange = M_PI/2;
        //颜色变换范围
    //    cell.greenRange = 1.0;
    //    cell.redRange = 1.0;
    //    cell.blueRange = 1.0;
        //颜色变换速度
    //    cell.redSpeed =-1.5;
    //    cell.blueSpeed =+1.5;
    //    cell.greenSpeed =+1.0;
    

      3)、添加粒子到发射层

    //单个粒子动画
    emitterLayer.emitterCells = @[cell];
    
    //多个粒子同时动画
    emitterLayer.emitterCells = @[cell,cell2];
    
    //粒子动画包含粒子动画(裂变的感觉)
    emitterLayer.emitterCells = @[cell];
    cell.emitterCells = @[cell2];
    cell2.emitterCells = @[cell3];
    

    注:

      在第三种情况,控制好 lifebirthRate 和 lifetime ,可以达到烟花效果。

      比如cell是发射效果(这里设为1个/秒吧)、cell3是爆炸效果(几十、几百个/秒),如果,cell3加在cell上,效果就像彗星移动(cell移动的过程中,cell3不断生成)。

      那么要实现发射效果,就要加个缓存层cell2。cell的生命周期(N秒) 和cell2的生成率(1个/N秒)一样 ,cell刚消失,cell2才产生。不过因为 lifebirthRate >= 1 。所以目前只有1秒符合。

    cell.lifebirthRate = 随意
    cell.lifetime = 1.0 + ?;
    
    cell2.lifebirthRate = 1.0;
    cell2.lifetime = 1.0 + ?;
    

    cell2的图片可以空,等到cell快消失前,生成一个cell2,同时cell2包含的cell3一秒几十、几百个生成,就是发射爆炸效果了。

    其他

      1、模态视图转场动画

    //设置模态视图的转场效果(如X信,朋友的历史单条说说,点击查看详细)。
    second.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
    //推
    [self presentViewController:second animated:YES completion:^{
    }];
    

       2、自定义转场动画(相当于快照当前屏幕)

        // 1、截图
        UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, YES, 0.0);
        [self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
        UIImage *coverImage = UIGraphicsGetImageFromCurrentImageContext();
    
        // 2、对截图,添加、置顶
        UIImageView *coverView = [[UIImageView alloc]initWithFrame:self.view.bounds];
        coverView.image = coverImage;
        [self.view addSubview:coverView];
        
        // 3、设置好动画后图层
        //   3-1、比如,淡入淡出,设置好该显示的View,再动画里,让“截图”透明为0。
        //   3-2、比如,是立方体cube效果,先旋转90度,再在动画里和“截图”一起旋转。
        
        // 4、动画
        [UIView animateWithDuration:1.0 animations:^{
            // 4-1、对截图进行动画
            
        } completion:^(BOOL finished) {
            // 4-2、完成、移除截图
            [coverView removeFromSuperview];
        }];
    


      3、动画所需的变换

        3-1、view:CGAffineTransform(缩放、位移、旋转。二维变换)

    // 角度 -> 弧度
    #define kDEGREES_TO_RADIANS(x)       ((x) / 180.0 * M_PI)
    
    // 获取正常的
    CGAffineTransform t = CGAffineTransformIdentity;
    
    // 带make,相对原始的,重复调用同一数据,不会变化
    CGAffineTransformMakeRotation(CGFloat angle)
    CGAffineTransformMakeScale(CGFloat sx, CGFloat sy)
    CGAffineTransformMakeTranslation(CGFloat tx, CGFloat ty)
    
    // 混合两个make变化
    CGAffineTransform t1 = CGAffineTransformMakeScale( 1.5, 1.5);
    CGAffineTransform t2 = CGAffineTransformMakeTranslation(50, 50);
    CGAffineTransform t  = CGAffineTransformConcat(t1, t2);
    
    // 在t的基础上,再变化。
    // 1、重复调用,可以不断变换。
    // 2、可以混合变化,同CGAffineTransformConcat
    CGAffineTransform t = self.view.transform;
    t = CGAffineTransformRotate(t, kDEGREES_TO_RADIANS(90.0));
    t = CGAffineTransformScale(t, 0.5, 0.5);
    t = CGAffineTransformTranslate(t, 50, 50);
    self.view.transform = t;
    
    // 反转,往相反的变换方向变换。
    // 1、如果是 Identity 不变换
    // 2、缩放0.5 / 左移50 / 旋转90度 ->(相对初始)缩放2.0 / 右移50 / 旋转-90
    // 3、所以变化 -> 反转 -> 变化,才能回到最初的状态
    CGAffineTransform t = test1.transform;
    t = CGAffineTransformInvert(t);
    test1.transform = t;
    
    // 初始变换函数
    //              [ a  b  0]
    //    [x y 1] * [ c  d  0] = [x' y' 1]
    //              [ tx ty 1]
    //
    //    x' = ax + cy + tx;
    //    y' = bx + dy + ty;
    //    
    //    CGAffineTransformMake(CGFloat a, CGFloat b,CGFloat c, CGFloat d, CGFloat tx, CGFloat ty);
    // 平移
    CGAffineTransform t = CGAffineTransformMake(1,0,0,1,tx,ty);
    // 缩放
    CGAffineTransform t = CGAffineTransformMake(x,0,0,y,0,0);
    // 旋转(计算有难度...)
    ...
    
    // 判断是否 原始
    CGAffineTransformIsIdentity
    // 判断是否 变换相等
    CGAffineTransformEqualToTransform
    

      3-2、layer:CATransform3D(缩放、位移、旋转。三维变换)

        1)、方法基本同“1-2、CGAffineTransform(缩放、位移、旋转。二维变换)”,不再赘述。

        2)、移动、缩放:多了z轴;旋转:绕哪个轴旋转。

        3)、最重要的一点,结构体的m34变量,控制透视效果。    

          m34 = -1.0 / d;  //d代表想象中视角和屏幕之间的距离,以像素为单位,大概就可以。通常500-1000。负号是方向。浮点不要漏!

        4)、view.layer.sublayerTransform

          设置后,所有子layer,围绕view.layer的position(anchorPoint)3D变换。如设置m34属性,有灭点效果。

          如单独设置子layer的transform,子layer各自绕自己的position(anchorPoint)3D变换。

        5)、想要看起来正常点、像是根据自身的position(anchorPoint),那就设置同样尺寸。

          layer.bounds = (CATransformLayer.bounds,有的话) = contentView.bounds。

        6)、view.layer.doubleSided 决定是否绘制视图背面,默认YES,转180度,有镜像效果。

        7)、用在layer的属性transform,还是扁平化,改变子layer的z轴,无效果,被压缩在父视图上。(搭配CATransformLayer才是真3D)。

        8)、假3D立方体效果,参照《iOS:小技巧》 -> “56、用6个view + CATransform3D 构建立方体” 。     

        9)、真3D立方体效果,参照《iOS:小技巧》 -> “57、用6个layer + CATransform3D + CATransformLayer 构建立方体” 。

      4、设置缺省动画(隐式动画) 

        0)、写在前面

          系统检索顺序:1)、- (nullable id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event

                   1-1)、(Layer)签代理,在需要的地方调用。

                   1-2)、(View)自定义View.m里重写(因为UIView签了代理,且默认禁掉隐式动画,而各种控件都继承于UIView)

                 2)、(Layer)self.testLayer.actions

                 3)、(没试)style字典

                 4)、(Layer)+ (id<CAAction>)defaultActionForKey:(NSString *)event

          禁止隐式动画,除了通过返回nil,也可以在 CATransaction 里实现,参照“2、CATransaction(Layer版的UIView Animation)”

        1)、- (nullable id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event

          1-1)、直接实现Animation

    - (nullable id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event
    {
        if ([event isEqualToString:@"backgroundColor"])
        {
            // 动画1
            // setBackgroundColor之前,会被系统调用。
            // 所以,fromValue = layer.backgroundColor。
            //    toValue不填,会自动赋上后来setBackgroundColor
            CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"backgroundColor"];
            animation.duration = 1.5;
            animation.fromValue = (__bridge id)layer.backgroundColor;
            return animation;
        }
        return nil;
        // 返回nil,继续查找action等。
        // 返回[NSNull null],不再查找。
    }
    

          1-2)、自定义Animation

    - (nullable id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event
    {
        if ([event isEqualToString:@"backgroundColor"])
        {       
            // 动画2
            MyAction *action = [[MyAction alloc]initWithOldBackgroundColor:layer.backgroundColor];
            return action;
        }
        return nil;
        // 返回nil,继续查找action等。
        // 返回[NSNull null],不再查找。
    }
    

          MyAction.h

    #import <Foundation/Foundation.h>
    #import <QuartzCore/CALayer.h>
    #import <QuartzCore/CAAnimation.h>
    
    @interface MyAction : NSObject <CAAction>{
        CGColorRef fromBgColor;
    }
    
    - (instancetype)initWithOldBackgroundColor:(CGColorRef)oldBgColor;
    
    @end
    

          MyAction.m

    #import "MyAction.h"
    #import <UIKit/UIKit.h>
    
    @implementation MyAction
    
    // 传动画前的状态
    - (instancetype)initWithOldBackgroundColor:(CGColorRef)oldBgColor
    {
        self = [super init];
        if (self) {
            fromBgColor = oldBgColor;
        }
        return self;
    }
    
    // 重写
    - (void)runActionForKey:(NSString *)event object:(id)anObject arguments:(nullable NSDictionary *)dict
    {
        // setBackgroundColor之后才被系统调用。
        // 所以,fromValue 需要传值进来。
        //    toValue 填上 setBackgroundColor 后的颜色
    
        CALayer *layer = (CALayer*)anObject;
       
        CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"backgroundColor"];
        animation.duration = 1.5;
        animation.fromValue = (__bridge id)fromBgColor;
        animation.toValue = (__bridge id)layer.backgroundColor;
    
        [layer addAnimation:animation forKey:@"test"];
    }
    
    @end
    

        补充:1、两者有所区别:

              返回动画,运行时还没被set背景颜色,所以 layer.backgroundColor 获取到之前的。

              返回签了CAAction协议的对象,是在set背景颜色之后调用的。

           2、如果是立方体、左推等动画转场效果,就不需要考虑是 调用之前、之后 的问题。直接设置就行。

        2)、(Layer)self.testLayer.actions

    self.testLayer.actions = @{@"backgroundColor":animation/*,@"sublayers":[NSNull null]*/};

    如果用 view 的 layer 属性↓,无效果,估计是因为view的隐性动画在“1)、”被禁。

    self.testView.layer.actions = @{@"backgroundColor":animation/*,@"sublayers":[NSNull null]*/};
    

        3)、(没试)style字典

        

        4)、(Layer)+ (id<CAAction>)defaultActionForKey:(NSString *)event

    + (id<CAAction>)defaultActionForKey:(NSString *)event
    {
        if ([event isEqualToString:@"backgroundColor"]) {
            CATransition *animation = [CATransition animation];
            animation.duration = 1.5;
            animation.type = @"cube";
            animation.subtype = kCATransitionFromRight;
            return animation;
        }
        return nil;
    }
    

      

  • 相关阅读:
    php函数注释
    组件化开发
    7.哪些工具可以帮助查找bug或进行静态分析
    6.Python中内存是如何管理的?
    5.Python是怎么解释的?
    4.pickling 和unpickling是什么?
    3.PEP 8是什么?
    2.Python是什么?使用Python的好处是什么?
    Redis介绍及字符串操作
    字符串转换为二进制
  • 原文地址:https://www.cnblogs.com/leonlincq/p/7412463.html
Copyright © 2020-2023  润新知