• iOS_40_核心动画


    核心动画之CATransition转场动画
    终于效果图:

    核心动画之CAKeyFrameAnimation,图标抖动效果
    终于效果图:

    核心动画之CAAnimationGroup(动画组)
    终于效果图:

    核心动画之CAKeyFrameAnimationGroup(关键帧动画)
    终于效果图:

    核心动画之CABasicAnimation(基本动画)
    终于效果图:

    UIView封装的动画,NSObject已经遵守了CAAnimationDelegate
    终于效果图:

    核心动画之幸运转盘
    终于效果图:

    核心动画之幸运转盘
    控制器
    //
    //  LuckyNumController.m
    //  40_核心动画
    //
    //  Created by beyond on 14-9-19.
    //  Copyright (c) 2014年 com.beyond. All rights reserved.
    //  幸运选号
    
    #import "LuckyNumController.h"
    #import "Circle.h"
    @interface LuckyNumController ()
    
    // 開始旋转
    - (IBAction)startRotate;
    // 停止旋转
    - (IBAction)stopRotate;
    - (IBAction)dismiss;
    // 要控制它的開始和停止
    @property (nonatomic, weak) Circle *circle;
    
    @end
    
    @implementation LuckyNumController
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        // 创建一个封装好的Circle,并加入到self.view
        Circle *circle = [Circle circle];
        circle.center = CGPointMake(self.view.frame.size.width * 0.5, self.view.frame.size.height * 0.5);
        [self.view addSubview:circle];
        self.circle = circle;
    }
    // 開始旋转
    - (IBAction)startRotate
    {
        // 调用其内部的方法 開始旋转
        [self.circle startRotating];
    }
    // 停止旋转
    - (IBAction)stopRotate
    {
        // 调用其内部的方法 停止旋转
        [self.circle stopRotating];
    }
    
    - (IBAction)dismiss
    {
        [self dismissViewControllerAnimated:YES completion:nil];
    }
    @end
    


    自己定义Circle及Xib
    //
    //  Circle.h
    //  40_核心动画
    //
    //  Created by beyond on 14-9-19.
    //  Copyright (c) 2014年 com.beyond. All rights reserved.
    //  大圆盘
    
    #import <UIKit/UIKit.h>
    
    @interface Circle : UIView
    
    // 构造方法,高速创建实例对象
    + (instancetype)circle;
    
    // 供外界调用,開始旋转
    - (void)startRotating;
    // 供外界调用,停止旋转
    - (void)stopRotating;
    
    @end
    


    //
    //  Circle.m
    //  40_核心动画
    //
    //  Created by beyond on 14-9-19.
    //  Copyright (c) 2014年 com.beyond. All rights reserved.
    //
    
    #import "Circle.h"
    #import "CircleBtn.h"
    
    @interface Circle()
    
    @property (weak, nonatomic) IBOutlet UIImageView *centerCircle;
    // 三步曲,切换button的选中状态
    @property (nonatomic, weak) CircleBtn *selectedBtn;
    // 时钟,控制centerCircle慢悠悠地转
    @property (nonatomic, strong) CADisplayLink *link;
    
    // 開始选号
    - (IBAction)startChoose;
    
    @end
    
    @implementation Circle
    #pragma mark - 生命周期方法
    // 构造方法,高速创建实例对象
    + (instancetype)circle
    {
        return [[[NSBundle mainBundle] loadNibNamed:@"Circle" owner:nil options:nil] lastObject];
    }
    
    // 从xib载入之后,一次性加入12个button
    // 在这种方法中,取得属性才是有值的
    - (void)awakeFromNib
    {
        // 让imageView能跟用户进行交互
        self.centerCircle.userInteractionEnabled = YES;
        // 一次性加入12个button
        [self add12Btns];
    }
    #define kAngleToRadius(angle) (angle)/180.0*M_PI
    // 一次性加入12个button
    - (void)add12Btns
    {
        // 一次性 加入12个button
        for (int i = 0; i < 12; i++) {
            // 1.创建一个button
            CircleBtn *btn = [CircleBtn buttonWithType:UIButtonTypeCustom];
            // 2.设置button的基本属性,而且依据i的不同,设置小button的旋转角度
            [self setBtnBasicAttribute:btn index:i];
            // 3.裁剪大图,并依据i的不同,从大图中不同位置rect,截取一个小图片
            [self setBtnBg:btn bigImg:@"LuckyAstrology" selectedBigImg:@"LuckyAstrologyPressed" index:i];
            // 4.监听button点击
            [btn addTarget:self action:@selector(btnClick:) forControlEvents:UIControlEventTouchDown];
            // 5.加入到self.view
            [self.centerCircle addSubview:btn];
            // 6.默认选中第0个button
            if (i == 0) {
                [self btnClick:btn];
            }
        }
    }
    // 抽取的方法,基本属性,而且依据i的不同,设置小button的旋转角度
    - (void)setBtnBasicAttribute:(UIButton *)btn index:(int)i
    {
        btn.bounds = CGRectMake(0, 0, 68, 143);
        // 设置button选中时的背景图片(红红的)
        [btn setBackgroundImage:[UIImage imageNamed:@"LuckyRototeSelected"] forState:UIControlStateSelected];
        // 为了所有的12个小button,可以排成一个圆形,位置同样,锚点为底边的中点,依据i不同,绕着锚点进行旋转
        // 位置所有同样,居中心
        btn.layer.position = CGPointMake(self.centerCircle.frame.size.width * 0.5, self.centerCircle.frame.size.height * 0.5);
        // 设置button图层的锚点(底边的中点)
        btn.layer.anchorPoint = CGPointMake(0.5, 1);
        // 设置button的旋转角度(依据i不同,绕着锚点进行旋转)
        // 角度转弧度 kAngleToRadius(angle) angle/180.0*M_PI
        // CGFloat angle = (30 * i) / 180.0 * M_PI;
        CGFloat angle =  kAngleToRadius(30*i);
        // 设置button的旋转角度
        btn.transform = CGAffineTransformMakeRotation(angle);
    
    }
    // 抽取的方法,依据i的不同,从大图中不同位置rect,截取一个小图片
    - (void)setBtnBg:(UIButton *)btn bigImg:(NSString *)bigImgName selectedBigImg:(NSString *)selectedBigImgName index:(int)i
    {
        // 1.载入大图片
        UIImage *bigImg = [UIImage imageNamed:bigImgName];
        UIImage *bigImgSelected = [UIImage imageNamed:selectedBigImgName];
        
        // 从大图片中裁剪中相应星座的图片(点坐标系 转成像素坐标系)
        CGFloat smallW = bigImg.size.width / 12  * [UIScreen mainScreen].scale;
        CGFloat smallH = bigImg.size.height * [UIScreen mainScreen].scale;
        
        // 2.依据i的不同,从大图中不同位置rect,截取一个小图片
        CGRect smallRect = CGRectMake(i * smallW, 0, smallW, smallH);
        // CGImageCreateWithImageInRect仅仅认像素
        // 截取出来的小图片 (默认状态下)
        CGImageRef smallImg = CGImageCreateWithImageInRect(bigImg.CGImage, smallRect);
        // 截取出来的小图片 (选中状态下)
        CGImageRef smallImgSelected = CGImageCreateWithImageInRect(bigImgSelected.CGImage, smallRect);
        // 3.设置button的默认和选中时的小图片
        [btn setImage:[UIImage imageWithCGImage:smallImg] forState:UIControlStateNormal];
        [btn setImage:[UIImage imageWithCGImage:smallImgSelected] forState:UIControlStateSelected];
    }
    
    // 监听button点击,三步曲,切换button的选中状态
    - (void)btnClick:(CircleBtn *)btn
    {
        self.selectedBtn.selected = NO;
        btn.selected = YES;
        self.selectedBtn = btn;
    }
    
    #pragma mark - 连线方法
    // 開始选号,即開始高速疯狂地旋转,
    - (IBAction)startChoose {
        // 1.先停止 慢悠悠地转
        [self stopRotating];
        
        // 2.期间,禁止与用户交互
        self.userInteractionEnabled = NO;
        
        // 3.開始疯狂地旋转
        CABasicAnimation *anim = [CABasicAnimation animation];
        anim.keyPath = @"transform.rotation";
        // by是相对值
        anim.byValue = @(2 * M_PI * 4);
        anim.duration = 1.5;
        // 时间函数,即 节奏, 开头和结尾比較慢,中间快
        anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
        // 设置代理,监听动画完毕状态
        anim.delegate = self;
        // 4.加入动画到图层
        [self.centerCircle.layer addAnimation:anim forKey:nil];
    
    }
    #pragma mark - 动画的代理方法
    // 高速旋转的动画停止时调用,2秒后,開始慢悠悠地转
    - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
    {
        // 恢复与用户交互
        self.userInteractionEnabled = YES;
        // 仅运行一次
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            NSLog(@"仅仅运行一次的代码");
        });
        // 延时1.2秒后,运行代码
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            [self startRotating];
        });
    }
    #pragma mark - 供外界调用
    // 供外界调用,開始不停地旋转
    - (void)startRotating
    {
        // 假设时钟有值,说明正在旋转,直接返回
        if (self.link) return;
        
        // 创建时钟,加入到主循环(1秒内刷新60次)
        CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self selector:@selector(update)];
        [link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
        self.link = link;
    }
    // 供外界调用,停止旋转,清空时钟
    - (void)stopRotating
    {
        [self.link invalidate];
        self.link = nil;
    }
    #pragma mark - 时钟方法
    // 慢慢旋转
    - (void)update
    {
        self.centerCircle.transform = CGAffineTransformRotate(self.centerCircle.transform, M_PI / 500);
    }
    @end
    



    自己定义CircleBtn
    //
    //  CircleBtn.m
    //  40_核心动画
    //
    //  Created by beyond on 14-9-19.
    //  Copyright (c) 2014年 com.beyond. All rights reserved.
    //
    
    #import "CircleBtn.h"
    
    @implementation CircleBtn
    
    // 调整button中的图片的位置
    - (CGRect)imageRectForContentRect:(CGRect)contentRect
    {
        CGFloat imageW = 40;
        CGFloat imageH = 47;
        CGFloat imageX = (contentRect.size.width - imageW) * 0.5;
        CGFloat imageY = 20;
        return CGRectMake(imageX, imageY, imageW, imageH);
    }
    // 取消默认的高亮状态
    - (void)setHighlighted:(BOOL)highlighted{}
    @end
    


    UIView封装的动画,NSObject已经遵守了CAAnimationDelegate
    //
    //  ViewAnimationController.m
    //  40_核心动画
    //
    //  Created by beyond on 14-9-19.
    //  Copyright (c) 2014年 com.beyond. All rights reserved.
    //  UIView封装的动画们
    
    #import "ViewAnimationController.h"
    
    @interface ViewAnimationController ()
    
    @property (weak, nonatomic) IBOutlet UIImageView *nanaImgView;
    @property (weak, nonatomic) IBOutlet UIView *singleView;
    // UIView封装的转场的动画
    - (IBAction)uiviewTransitionAnimation;
    // UIView封装的单纯的动画
    - (IBAction)uiviewAnimation;
    
    // 图层的动画
    - (IBAction)calayerAnimation;
    
    - (IBAction)dismiss;
    
    @property (nonatomic, assign) int index;
    @end
    
    @implementation ViewAnimationController
    
    
    // UIView封装的转场的动画
    - (IBAction)uiviewTransitionAnimation
    {
        self.index++;
        if (self.index == 7) {
            self.index = 0;
        }
        
        NSString *filename = [NSString stringWithFormat:@"%d.png", self.index ];
        self.nanaImgView.image = [UIImage imageNamed:filename];
        UIViewAnimationOptions type = [self typeFromIndex:self.index];
        [UIView transitionWithView:self.view duration:1.0 options:type animations:nil completion:nil];
    }
    // UIView封装的单纯的动画
    - (IBAction)uiviewAnimation
    {
        // 方式1:使用一前一后夹住
        [UIView beginAnimations:nil context:nil];
        // 动画运行完成后, 能够手动监听,也能够用NSObject默认的方法监听
        //    [UIView setAnimationDelegate:self];
        //    [UIView setAnimationDidStopSelector:@selector(animateStop)];
        self.singleView.center = CGPointMake(200, 300);
        [UIView commitAnimations];
        
        // 方式2:使用block
        [UIView animateWithDuration:1.0 animations:^{
            self.singleView.center = CGPointMake(200, 300);
        } completion:^(BOOL finished) {
            
        }];
    }
    // 图层的动画
    - (IBAction)calayerAnimation
    {
        // 1.创建基本动画
        CABasicAnimation *anim = [CABasicAnimation animation];
        // 2.设置动画属性
        anim.keyPath = @"position";
        anim.toValue = [NSValue valueWithCGPoint:CGPointMake(50, 50)];
        anim.duration = 2.0;
        /**让图层保持动画运行完成后的状态**/
        // 动画运行完成后不要删除动画
        anim.removedOnCompletion = NO;
        anim.fillMode = kCAFillModeForwards;
        // 设置动画的代理,能够监听动画的运行阶段
        anim.delegate = self;
        
        // 3.加入动画到图层,图层动画都是假象, 在动画运行过程中, 图层的position属性一直都没有变过
        [self.singleView.layer addAnimation:anim forKey:nil];
        
        //    self.singleView.layer.position == CGPointMake(0, 0)
    }
    
    - (IBAction)dismiss
    {
        [self dismissViewControllerAnimated:YES
                                 completion:nil];
    }
    #pragma mark - NSObject自己主动实现了动画的delegate
    - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
    {
        NSLog(@"动画已停止....%@", NSStringFromCGPoint(self.singleView.layer.position));
        self.singleView.hidden = YES;
    }
    
    /*
     UIViewAnimationOptionCurveEaseInOut            = 0 << 16, // default
     UIViewAnimationOptionCurveEaseIn               = 1 << 16,
     UIViewAnimationOptionCurveEaseOut              = 2 << 16,
     UIViewAnimationOptionCurveLinear               = 3 << 16,
     
     UIViewAnimationOptionTransitionNone            = 0 << 20, // default
     UIViewAnimationOptionTransitionFlipFromLeft    = 1 << 20,
     UIViewAnimationOptionTransitionFlipFromRight   = 2 << 20,
     UIViewAnimationOptionTransitionCurlUp          = 3 << 20,
     UIViewAnimationOptionTransitionCurlDown        = 4 << 20,
     UIViewAnimationOptionTransitionCrossDissolve   = 5 << 20,
     UIViewAnimationOptionTransitionFlipFromTop     = 6 << 20,
     UIViewAnimationOptionTransitionFlipFromBottom  = 7 << 20,
     */
    - (UIViewAnimationOptions)typeFromIndex:(int)index
    {
        switch (index%7) {
            case 0:
                return UIViewAnimationOptionTransitionFlipFromLeft;
                break;
            case 1:
                return UIViewAnimationOptionTransitionFlipFromRight;
                break;
            case 2:
                return UIViewAnimationOptionTransitionCurlUp;
                break;
            case 3:
                return UIViewAnimationOptionTransitionCurlDown;
                break;
            case 4:
                return UIViewAnimationOptionTransitionCrossDissolve;
                break;
            case 5:
                return UIViewAnimationOptionTransitionFlipFromTop;
                break;
            case 6:
                return UIViewAnimationOptionTransitionFlipFromBottom;
                break;
                
                
            default:
                break;
        }
        return 0;
    }
    @end
    


    经常使用UIViewAnimationOptions例如以下:

    核心动画之CABasicAnimation(基本动画)
    //
    //  BasicAnimationController.m
    //  40_核心动画
    //
    //  Created by beyond on 14-9-18.
    //  Copyright (c) 2014年 com.beyond. All rights reserved.
    //  核心动画之基本动画,仅仅有fromValue和toValue
    
    #import "BasicAnimationController.h"
    // 图层相关处理
    #import <QuartzCore/QuartzCore.h>
    @interface BasicAnimationController ()
    // 向主控制器的view的主图层上 加入一个子图层(含图片的层)
    @property (nonatomic, strong) CALayer *nanaLayer;
    
    // 基本动画之 平移
    - (IBAction)translateAnimation;
    // 基本动画之 旋转
    - (IBAction)rotationAnimation;
    // 基本动画之 缩放
    - (IBAction)scaleAnimation;
    // 基本动画之 形变
    - (IBAction)transformAnimation;
    
    - (IBAction)dismiss;
    
    @end
    
    
    
    @implementation BasicAnimationController
    #pragma mark - 生命周期
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        
        // 向主控制器的view的主图层上 加入一个子图层(含图片的层)
        [self addSubLayer];
    }
    // 向主控制器的view的主图层上 加入一个子图层(含图片的层)
    - (void)addSubLayer
    {
        CALayer *nanaLayer = [CALayer layer];
        nanaLayer.position = CGPointMake(160, 133);
        nanaLayer.bounds = CGRectMake(0, 0, 150, 150);
        nanaLayer.backgroundColor = [UIColor redColor].CGColor;
        // 重要~~~一个含图片的子图层
        nanaLayer.contents = (id)[UIImage imageNamed:@"nanaLogo.jpg"].CGImage;
        // 圆角
        nanaLayer.cornerRadius = 10;
        nanaLayer.masksToBounds = YES;
        // 控制器的view的主图层 中加入子图层
        [self.view.layer addSublayer:nanaLayer];
        self.nanaLayer = nanaLayer;
    }
    #pragma mark - 连线方法
    
    // 基本动画之 平移
    - (IBAction)translateAnimation
    {
        // 1.创建动画对象
        CABasicAnimation *anim = [CABasicAnimation animation];
        
        // 2.设置动画对象的属性
        // keyPath决定了运行如何的动画, 调整哪个属性来运行动画
        anim.keyPath = @"position";
        //    anim.fromValue = [NSValue valueWithCGPoint:CGPointMake(0, 0)];
        // toValue : 终于变成什么值
        // byValue : 添加多少值
        anim.byValue = [NSValue valueWithCGPoint:CGPointMake(30, 40)];
        anim.duration = 2.0;
        
        /**让图层保持动画运行完成后的状态**/
        // 动画运行完成后不要删除动画
        anim.removedOnCompletion = NO;
        // 保持最新的状态
        anim.fillMode = kCAFillModeForwards;
        
        // 3.最后一步。加入动画到图层
        [self.nanaLayer addAnimation:anim forKey:nil];
    }
    // 基本动画之 旋转
    - (IBAction)rotationAnimation
    {
        // 1.创建动画对象
        CABasicAnimation *anim = [CABasicAnimation animation];
        
        // 2.设置动画对象的属性
        // keyPath决定了运行如何的动画, 调整哪个属性来运行动画
        anim.keyPath = @"transform";
        //    anim.fromValue = [NSValue valueWithCGPoint:CGPointMake(0, 0)];
        // 重要~~~參数:角度,x,y,z
        anim.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(M_PI, 1, -1, 0)];
        anim.duration = 2.0;
        /**让图层保持动画运行完成后的状态**/
        // 动画运行完成后不要删除动画
        anim.removedOnCompletion = NO;
        anim.fillMode = kCAFillModeForwards;
        
        // 3.最后一步,加入动画到图层
        [self.nanaLayer addAnimation:anim forKey:nil];
    }
    // 基本动画之 缩放
    - (IBAction)scaleAnimation
    {
        // 1.创建动画对象
        CABasicAnimation *anim = [CABasicAnimation animation];
        
        // 2.设置动画对象的属性
        // keyPath决定了运行如何的动画, 调整哪个属性来运行动画
        anim.keyPath = @"bounds";
        //    anim.fromValue = [NSValue valueWithCGPoint:CGPointMake(0, 0)];
        anim.toValue = [NSValue valueWithCGRect:CGRectMake(0, 0, 250, 250)];
        anim.duration = 2.0;
        
        /**让图层保持动画运行完成后的状态**/
        // 动画运行完成后不要删除动画
        anim.removedOnCompletion = NO;
        // 保持最新的状态
        anim.fillMode = kCAFillModeForwards;
        
        // 3.最后一步。加入动画到图层
        [self.nanaLayer addAnimation:anim forKey:nil];
    }
    // 基本动画之 形变
    - (IBAction)transformAnimation
    {
        // 1.创建动画对象
        CABasicAnimation *anim = [CABasicAnimation animation];
        
        // 2.设置动画对象的属性
        // keyPath决定了运行如何的动画, 调整哪个属性来运行动画
        //    anim.keyPath = @"transform.rotation";
        //    anim.keyPath = @"transform.scale.x";
        anim.keyPath = @"transform.translation.y";
        anim.toValue = @(100);
        //    anim.toValue = [NSValue valueWithCGPoint:CGPointMake(100, 100)];
        anim.duration = 2.0;
        
        /**让图层保持动画运行完成后的状态**/
        // 动画运行完成后不要删除动画
        anim.removedOnCompletion = NO;
        anim.fillMode = kCAFillModeForwards;
        
        // 3.最后一步,加入动画到图层
        [self.nanaLayer addAnimation:anim forKey:nil];
    }
    
    - (IBAction)dismiss
    {
        [self dismissViewControllerAnimated:YES completion:nil  ];
    }
    @end
    


    核心动画之CAKeyFrameAnimationGroup(关键帧动画)
    #pragma mark - 两个关键帧动画演示
    // 关键帧动画演示_1   沿着圆形路径移动
    - (IBAction)keyFrameAnimation_1
    {
        // 1.创建 核心动画之关键帧动画:KeyframeAnimation
        CAKeyframeAnimation *anim = [CAKeyframeAnimation animation];
        // 2.设置动画属性
        anim.keyPath = @"position";
        anim.duration = 2.0;
        // 锚点为左上角
        self.nanaImgView.layer.anchorPoint = CGPointMake(0.5, 0.5);
    
        // 关键两句代码~~~保持动画运行完成后的状态
        anim.removedOnCompletion = NO;
        anim.fillMode = kCAFillModeForwards;
        
        // 指定义动画的路径:沿着圆形路径移动
        CGMutablePathRef path = CGPathCreateMutable();
        CGPathAddEllipseInRect(path, NULL, CGRectMake(50, 50, 200, 200));
        anim.path = path;
        // C语言中,创建就要释放
        CGPathRelease(path);
        
        // 动画的时间函数:就是设置动画的运行节奏
        // kCAMediaTimingFunctionEaseInEaseOut : 一開始比較慢, 中间会加速,  临近结束的时候, 会变慢
        anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
        // 设置动画的代理,目的是监听动画的開始、结束等状态
        // 默认NSObject已经遵守了协议CAAnimationDelegate
        anim.delegate = self;
        // 3.最后一步,加入动画到图层
        [self.nanaImgView.layer addAnimation:anim forKey:nil];
    }
    // 关键帧动画演示_2
    - (IBAction)keyFrameAnimation_2
    {
        // CABasicAnimation  仅仅支持两个值:fromValue --> toValue
        // 1.创建 核心动画之关键帧动画:KeyframeAnimation
        CAKeyframeAnimation *anim = [CAKeyframeAnimation animation];
        // 2.设置动画属性
        anim.keyPath = @"position";
        anim.duration = 2.0;
        // 锚点为左上角
        self.nanaImgView.layer.anchorPoint = CGPointMake(0, 0);
        
        NSValue *v1 = [NSValue valueWithCGPoint:CGPointZero];
        NSValue *v2 = [NSValue valueWithCGPoint:CGPointMake(170, 0)];
        NSValue *v3 = [NSValue valueWithCGPoint:CGPointMake(170, 330)];
        NSValue *v4 = [NSValue valueWithCGPoint:CGPointMake(0, 330)];
        NSValue *v5 = [NSValue valueWithCGPoint:CGPointZero];
        anim.values = @[v1, v2, v3, v4,v5];
        
        //    anim.keyTimes = @[@(0.5), @(0.25), @(0.25)];
        
        // 关键两句代码~~~保持动画运行完成后的状态
        anim.removedOnCompletion = NO;
        anim.fillMode = kCAFillModeForwards;
        // 3.最后一步,加入动画到图层
        [self.nanaImgView.layer addAnimation:anim forKey:nil];
    }
    #pragma mark - 某个动画的代理方法
    // 动画開始的时候调用
    - (void)animationDidStart:(CAAnimation *)anim
    {
        NSLog(@"animationDidStart");
    }
    // 动画结束的时候调用
    - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
    {
        NSLog(@"animationDidStop");
    }


    核心动画之CAAnimationGroup(动画组)
    // 角度转弧度公式,角*180/PI
    #define kAngle2Radius(angle) ((angle) / 180.0 * M_PI)
    // 開始同一时候播放多个动画(动画组)
    - (IBAction)animationGroup
    {
        // 1.创建旋转动画对象
        CABasicAnimation *rotate = [CABasicAnimation animation];
        // 动画作用于 旋转角度
        rotate.keyPath = @"transform.rotation";
        rotate.toValue = @(M_PI*4);
        
        // 2.创建缩放动画对象
        CABasicAnimation *scale = [CABasicAnimation animation];
        // 动画作用于 缩放
        scale.keyPath = @"transform.scale";
        scale.toValue = @(0.0);
        
        // 3.平移动画
        CABasicAnimation *move = [CABasicAnimation animation];
        // 动画作用于 平移位置
        move.keyPath = @"transform.translation";
        move.toValue = [NSValue valueWithCGPoint:CGPointMake(100, 100)];
        
        // 4.将全部的动画加入到动画组中
        CAAnimationGroup *group = [CAAnimationGroup animation];
        group.animations = @[rotate, scale, move];
        group.duration = 2.0;
        // 关键两句代码~~~保持动画运行完成后的状态
        group.removedOnCompletion = NO;
        group.fillMode = kCAFillModeForwards;
        // 3.加入动画到图层,key仅仅是为了将来控制它停止时才用到
        [self.nanaImgView.layer addAnimation:group forKey:nil];
    }
    


    核心动画之CAKeyFrameAnimation,图标抖动效果
    //
    //  IconShakeController.m
    //  40_核心动画
    //
    //  Created by beyond on 14-9-18.
    //  Copyright (c) 2014年 com.beyond. All rights reserved.
    //  核心动画之图标抖动
    
    #import "IconShakeController.h"
    
    // 角度转弧度公式,角*180/PI
    #define kAngle2Radius(angle) ((angle) / 180.0 * M_PI)
    
    
    
    @interface IconShakeController ()
    
    @property (weak, nonatomic) IBOutlet UIImageView *nanaImgView;
    // 開始抖动
    - (IBAction)startShake;
    // 停止抖动
    - (IBAction)stopShake;
    - (IBAction)dismiss;
    
    @end
    
    @implementation IconShakeController
    
    // 開始抖动
    - (IBAction)startShake
    {
        // 1.创建 核心动画之关键帧动画:KeyframeAnimation
        CAKeyframeAnimation *anim = [CAKeyframeAnimation animation];
        
        // 2.设置动画属性
        // 左旋转,右旋转
        anim.keyPath = @"transform.rotation";
        // 数组
        anim.values = @[@(kAngle2Radius(-3)),  @(kAngle2Radius(3)), @(kAngle2Radius(-3))];
        // 总计用时
        anim.duration = 0.15;
        // 动画的反复运行次数:一直抖动
        anim.repeatCount = MAXFLOAT;
        
        // 关键两句代码~~~保持动画运行完成后的状态
        anim.removedOnCompletion = NO;
        anim.fillMode = kCAFillModeForwards;
        // 3.加入动画到图层,key是为了将来控制它停止时用到
        [self.nanaImgView.layer addAnimation:anim forKey:@"iconShake"];
    }
    // 停止抖动
    - (void)stopShake
    {
        // 为图层加入动画时,传入參数key,是为了控制它停止时用到
        [self.nanaImgView.layer removeAnimationForKey:@"iconShake"];
    }
    
    - (IBAction)dismiss
    {
        [self dismissViewControllerAnimated:YES completion:nil];
    }
    @end
    


    核心动画之CATransition转场动画
    //
    //  TransitionController.m
    //  40_核心动画_转场动画
    //
    //  Created by beyond on 14-9-18.
    //  Copyright (c) 2014年 com.beyond. All rights reserved.
    //  核心动画之 转场动画
    
    #import "TransitionController.h"
    
    
    @interface TransitionController ()
    
    // 正在显示的图片的索引
    @property (nonatomic, assign) int index;
    // 界面上的imageView
    @property (weak, nonatomic) IBOutlet UIImageView *nanaImgView;
    // 显示上一张图片
    - (IBAction)previous;
    // 显示下一张图片
    - (IBAction)next;
    
    - (IBAction)dismiss;
    
    @end
    
    @implementation TransitionController
    
    // 显示上一张图片
    - (IBAction)previous
    {
        self.index--;
        if (self.index == -1) {
            self.index = 9;
        }
        
        NSString *filename = [NSString stringWithFormat:@"%d.png", self.index];
        self.nanaImgView.image = [UIImage imageNamed:filename];
        
        
        CATransition *anim = [CATransition animation];
        anim.type = [self typeFromIndex:self.index];
        //    anim.type = @"cube";
        //    anim.subtype = kCATransitionFromLeft;
        // anim.type = kCATransitionFade;
        anim.duration = 2;
        [self.view.layer addAnimation:anim forKey:nil];
    }
    
    // 显示下一张图片
    - (IBAction)next
    {
        self.index++;
        if (self.index == 10) {
            self.index = 0;
        }
        
        NSString *filename = [NSString stringWithFormat:@"%d.png", self.index];
        self.nanaImgView.image = [UIImage imageNamed:filename];
        
        // 转场动画
        CATransition *anim = [CATransition animation];
        anim.type = [self typeFromIndex:self.index];
        //    anim.type = @"pageCurl";
        //    anim.subtype = kCATransitionFromRight;
        anim.duration = 2;
        
        //    anim.startProgress = 0.0;
        //
        //    anim.endProgress = 0.5;
        
        [self.view.layer addAnimation:anim forKey:nil];
    }
    
    // 自己定义方法,随机生成 不同的转场效果
    /*
     1.#define定义的常量
     kCATransitionFade   交叉淡化过渡
     kCATransitionMoveIn 新视图移到旧视图上面
     kCATransitionPush   新视图把旧视图推出去
     kCATransitionReveal 将旧视图移开,显示以下的新视图
     
     2.用字符串表示
     pageCurl            向上翻一页
     pageUnCurl          向下翻一页
     rippleEffect        滴水效果
     suckEffect          收缩效果,如一块布被抽走
     cube                立方体效果
     oglFlip             上下翻转效果
     
     */
    - (NSString *)typeFromIndex:(int)index
    {
        switch (index%10) {
            case 0:
                return kCATransitionFade;
                break;
            case 1:
                return kCATransitionMoveIn;
                break;
            case 2:
                return kCATransitionPush;
                break;
            case 3:
                return kCATransitionReveal;
                break;
            case 4:
                return @"pageCurl";
                break;
            case 5:
                return @"pageUnCurl";
                break;
            case 6:
                return @"rippleEffect";
                break;
            case 7:
                return @"suckEffect";
                break;
            case 8:
                return @"cube";
                break;
            case 9:
                return @"oglFlip";
                break;
                
                
            default:
                break;
        }
        return nil;
    }
    - (IBAction)dismiss
    {
        [self dismissViewControllerAnimated:YES completion:nil];
    }
    
    @end
    


    CALayer与UIView之间的关系

    UIView本身不可视,其内部的CALayer才具有显示功能

    改变CALayer的属性,就是改变UIView的外观(如圆角、阴影)

    CALayer的经常使用属性(如位置、锚点)

    CALayer的经常使用属性(如边框、圆角)

    UIColor和UIImage必须转成能够跨平台的CGColorRef、CGImageRef

    UIView与CALayer重要差别是:能与用户交互

    锚点就是处在父层的position坐标处的那个点

    红色图层的锚点为(0,0)时的情况

    红色图层的锚点为(0.5,0)时的情况

    红色图层的锚点为(1,0.5)时的情况

    红色图层的锚点为(0.5,0.5)时的情况(默认值)

    创建子图层

    UIView内部默认有个CALayer对象(根层),通过layer属性能够訪问这个根层。

    要注意的是,这个默认的层不同意又一次创建,但能够往层里面加入子层

    * UIView能够通过addSubview:方法加入子视图

    类似地。CALayer能够通过addSublayer:方法加入子层

    接下来演示一下怎样加入子层:


    一、加入一个简单的图层

     1 CALayer *myLayer = [CALayerlayer];

     2// 设置层的宽度和高度(100x100)

     3 myLayer.bounds = CGRectMake(0,0, 100, 100);

     4// 设置层的位置

     5 myLayer.position = CGPointMake(100,100);

     6// 设置层的背景颜色:红色

     7 myLayer.backgroundColor = [UIColor redColor].CGColor;

     8// 设置层的圆角半径为10

     9 myLayer.cornerRadius =10;

    10 

    11// 加入myLayer到控制器的view的根层layer中

    12[self.view.layer addSublayer:myLayer];



    * 第1行创建了一个自己主动释放的CALayer对象,你也能够使用经典的alloc和init方法来创建

    * 第12行将创建好的层加入到控制器的view的根层


     


    二、加入一个显示图片的图层



     1 CALayer *myLayer = [CALayerlayer];

     2// 设置层的宽度和高度(100x100)

     3 myLayer.bounds = CGRectMake(0,0, 100, 100);

     4// 设置层的位置

     5 myLayer.position = CGPointMake(100,100);

     6// 设置须要显示的图片

     7 myLayer.contents = (id)[UIImage imageNamed:@"nana.png"].CGImage;

     8// 设置层的圆角半径为10

     9 myLayer.cornerRadius =10;

    10// 假设设置了图片,须要设置这个属性为YES才有圆角效果

    11 myLayer.masksToBounds = YES;

    12 

    13// 加入myLayer到控制器的view的layer中

    14[self.view.layer addSublayer:myLayer];



    * 在第7行设置须要显示的图片,

    注意,这里用的是UIImage的CGImage属性,是一种CGImageRef类型的数据


     


    三、为什么CALayer中使用CGColorRef

    和CGImageRef这2种数据类型,

    而不用UIColor和UIImage?

    * 首先要知道:CALayer是定义在QuartzCore框架中的;CGImageRef、CGColorRef两种数据类型是定义在CoreGraphics框架中的;UIColor、UIImage是定义在UIKit框架中的

    * 其次,QuartzCore框架和CoreGraphics框架是能够跨平台使用的,在iOS和Mac OS X上都能使用,可是UIKit仅仅能在iOS中使用

    * 因此。为了保证可移植性,QuartzCore不能使用UIImage、UIColor。仅仅能使用CGImageRef、CGColorRef

    * 只是非常多情况下,能够通过UIKit对象的特定方法,得到CoreGraphics对象,比方UIImage的CGImage方法能够返回一个CGImageRef

     


    四、UIView和CALayer的选择

    前面的2个效果不仅能够通过加入层来实现。还能够通过加入UIView来实现。

    比方,第1个红色的层能够用一个UIView来实现,

    第2个显示图片的层能够用一个UIImageView来实现。

    既然CALayer和UIView都能实现同样的显示效果,那到底该选择谁好呢?

    * 事实上,对照CALayer,UIView多了一个事件处理的功能。

    也就是说,CALayer不能处理用户的触摸事件。而UIView能够

    * 所以,假设显示出来的东西须要跟用户进行交互的话,用UIView;

    假设不须要跟用户进行交互,用UIView或者CALayer都能够

    * 当然。CALayer的性能会高一些,由于它少了事件处理的功能,更加轻量级

     


    五、UIView和CALayer的其它关系

    * UIView能够通过subviews属性訪问全部的子视图

    类似地。CALayer也能够通过sublayers属性訪问全部的子层

    * UIView能够通过superview属性訪问父视图

    类似地,CALayer也能够通过superlayer属性訪问父层

    * 以下再看一张UIView和CALayer的关系图:

    假设两个UIView是父子关系,那么它们内部的CALayer也是父子关系。

    即:父子UIView内部的根层也是父子层的关系


    重要~自己定义图层

    自己定义图层。事实上就是在层上画图,一共同拥有2种方法:


    一、自己定义层的方法1

    方法描写叙述:创建一个CALayer的子类,然后覆盖drawInContext:方法,使用Quartz2D API进行画图

    1.创建一个CALayer的子类,继承自CALayer


     

    2.在.m文件里覆盖drawInContext:方法。在里面画图



     1@implementation MyLayer

     2 

     3#pragma mark 绘制一个实心三角形

     4 - (void)drawInContext:(CGContextRef)ctx {

     5    // 设置为蓝色(后四个參数是:RGBA)

     6     CGContextSetRGBFillColor(ctx,0, 0, 1, 1);

     7 

     8     

     9    // 设置三角形的起点

    10     CGContextMoveToPoint(ctx,50, 0);

    11    // 从(50, 0)连线到(0, 100)

    12     CGContextAddLineToPoint(ctx,0, 100);

    13    // 从(0, 100)连线到(100, 100)

    14     CGContextAddLineToPoint(ctx,100, 100);

    15    // 合并路径。连接起点和终点

    16    CGContextClosePath(ctx);

    17     

    18    // 绘制路径

    19    CGContextFillPath(ctx);

    20 }

    21 

    22 @end



     

    3.在控制器中加入图层到屏幕上



    1 MyLayer *layer = [MyLayer layer];

    2// 设置层的宽高

    3 layer.bounds = CGRectMake(0,0, 100, 100);

    4// 设置层的位置

    5 layer.position = CGPointMake(100,100);

    6// 開始绘制图层(内部调用drawRect方法)

    7[layer setNeedsDisplay];

    8 [self.view.layer addSublayer:layer];



    注意第7行,必需要调用setNeedsDisplay这种方法,才会触发drawInContext:方法的调用,然后进行画图


     


    二、自己定义层的方法2

    方法描写叙述:

    设置CALayer的delegate,然后让delegate实现drawLayer:inContext:方法。

    当CALayer须要画图时,会调用delegate的drawLayer:inContext:方法进行画图。

    * 这里要注意的是:

    不能再将某个UIView设置为CALayer的delegate

    由于UIView对象已经是它内部根图层的delegate,

    再次设置为其它层的delegate就会出问题

    UIView和它内部CALayer的默认关系图:


    可见,UIView的根层的代理就是UIView自己


    1.创建新的子图层。设置delegate为当前控制器,然后加入到控制器的view的layer中



     1 CALayer *layer = [CALayer layer];

     2// 设置delegate为当前控制器

     3layer.delegate = self;

     4// 设置层的宽高

     5 layer.bounds = CGRectMake(0,0, 100, 100);

     6// 设置层的位置

     7 layer.position = CGPointMake(100,100);

     8// 開始绘制图层(此时会调用代理的drawLayer方法)

     9[layer setNeedsDisplay];

    10[self.view.layer addSublayer:layer];



    * 在第3行设置了CALayer的delegate,这里的self是指当前控制器

    * 注意第9行,须要调用setNeedsDisplay这种方法,才会调用代理delegate的drawLayer:inContext:方法进行画图

     

    2.让CALayer的delegate(即当前控制器)实现drawLayer:inContext:方法



     1#pragma mark 画一个矩形框

     2 - (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {

     3    // 设置颜色蓝色(參数是RGBA)

     4     CGContextSetRGBStrokeColor(ctx,0, 0, 1, 1);

     5    // 设置边框宽度

     6     CGContextSetLineWidth(ctx,10);

     7     

     8    // 加入一个跟层一样大的矩形到路径中

     9    CGContextAddRect(ctx, layer.bounds);

    10     

    11    // 绘制路径

    12    CGContextStrokePath(ctx);

    13




     


     三、其它

    1.总结

    不管採取哪种方法来自己定义层。继承或设置代理

    都必须调用CALayer的setNeedsDisplay方法才干正常画图。

     

    2.UIView的具体显示过程

    * 当UIView须要显示时。它内部的根层会准备好一个CGContextRef(图形上下文),然后调用根层的delegate(这里就是UIView)的drawLayer:inContext:方法,而且传入已经准备好的CGContextRef对象。

    而UIView在drawLayer:inContext:方法中又会调用自己的drawRect:方法

    从而终于:仅仅需在drawRect方法中写代码就可以

    * 平时在drawRect:中通过UIGraphicsGetCurrentContext()获取的就是由根层传入的CGContextRef对象,在drawRect:中完毕的全部画图都会填入到根层的CGContextRef中,最后才被拷贝至屏幕,进行显示


    CALayer基本属性演示:
    //
    //  LayerController.m
    //  40_核心动画
    //
    //  Created by beyond on 14-9-19.
    //  Copyright (c) 2014年 com.beyond. All rights reserved.
    /*
     以下是大实话:
     UIView内部有一个根层
     UIView是内部根层的delegate
     1.根层会准备好一个 图层上下文
     2.根层会调用代理(UIView)的drawLayer方法
     而且将1.中准备好的图层上下文传进来
     3.UIView的drawLayer方法,内部,会调用drawRect方法
     因此,经常使用的drawRect方法中的全部东东,
     全部被绘制到了 根层准备好的这个图层上下文中,
     所以说,UIView中drawRect方法所画的全部东东,全画到了根层的上下文中
     */
    
    /*
     自己定义图层:
     方式1:
     子类,继承CALayer,实现drawLayer:inContext:方法
     方式2:
     在控制器中,直接创建一个子图层,设置其代理为当前控制器,
     让代理实现的drawLayer方法中 画东东
     注意:不管哪一种,都要手动调用子图层的setNeedsDisplay方法
     */
    
    // UIView *view;
    // UIView的根层的代理,已经是它自己了
    // view.layer.delegate == view;
    
    // view的完整显示过程
    // 1. view.layer会准备一个Layer Graphics Contex(图层类型的上下文)
    // 2. 调用view.layer.delegate(也就是view)的drawLayer:inContext:,并传入刚才准备好的图层上下文
    // 3. view的drawLayer:inContext:方法内部又会调用view的drawRect:方法(就是我们经常写自己定义画图代码的方法)
    // 4. view就能够在drawRect:方法中实现画图代码, 全部东西终于都绘制到view.layer上面
    // 5. 系统再将view.layer的内容复制到屏幕, 于是完毕了view的显示
    
    
    
    #import "LayerController.h"
    #import "NanaLayer.h"
    @interface LayerController ()
    - (IBAction)dismiss;
    - (IBAction)diyCALayer_1;
    - (IBAction)diyCALayer_2;
    
    @property (nonatomic, strong) CALayer *layer;
    
    // 隐式动画,即,改变CALayer的一些属性值,默认就会有动画效果
    - (IBAction)layerAnimation;
    
    
    
    @property (weak, nonatomic) IBOutlet UIView *purpleView;
    @property (weak, nonatomic) IBOutlet UIImageView *nanaImgView;
    // 演示CALayer的基本属性
    - (IBAction)layerBasicAttribute_1;
    - (IBAction)layerBasicAttribute_2;
    - (IBAction)layerBasicAttribute_3;
    
    @end
    
    @implementation LayerController
    
    
    
    
    #pragma mark - 连线方法
    // 自己定义图层方式1:使用子类 图层,覆盖父类CALayer的drawLayer,进行画图
    - (IBAction)diyCALayer_1
    {
        NanaLayer *layer = [NanaLayer layer];
        layer.bounds = CGRectMake(0, 0, 100, 100);
        layer.backgroundColor = [UIColor blueColor].CGColor;
        layer.anchorPoint = CGPointZero;
        // 不管哪种自己定义图层,都必须 调用图层的setNeedsDisplay方法,才干够显示
        [layer setNeedsDisplay];
        [self.view.layer addSublayer:layer];
    }
    // 自己定义图层方式2:通过设置代理,调用代理的drawLayer方法,进行画图
    - (IBAction)diyCALayer_2
    {
        CALayer *layer = [CALayer layer];
        layer.bounds = CGRectMake(0, 0, 100, 100);
        layer.backgroundColor = [UIColor blackColor].CGColor;
        layer.anchorPoint = CGPointZero;
        layer.position = CGPointMake(100, 100);
        // 设置新建的图层代理,让代理实现drawLayer方法
        // layer.delegate = self;(不知道为啥会崩溃?

    ??) // 不管哪种自己定义图层,都必须 调用图层的setNeedsDisplay方法,才干够显示 [layer setNeedsDisplay]; [self.view.layer addSublayer:layer]; } #pragma mark - 图层的代理方法 // 自己定义图层方式2:通过设置代理,调用代理的drawLayer方法,进行画图 - (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx { // 设置RGB颜色 CGContextSetRGBFillColor(ctx, 1, 0, 0, 1); // 画个rect CGContextAddRect(ctx, CGRectMake(0, 0, 20, 20)); // 闭合路径 CGContextFillPath(ctx); } - (IBAction)dismiss { [self dismissViewControllerAnimated:NO completion:nil]; } #pragma mark - CALayer隐式动画 // 隐式动画,即,改变CALayer的一些属性值,默认就会有动画效果 - (IBAction)layerAnimation { // 创建一个layer并加入,2秒后,运行隐式动画 CALayer *layer = [CALayer layer]; layer.bounds = CGRectMake(0, 0, 100, 100); layer.backgroundColor = [UIColor redColor].CGColor; layer.position = CGPointZero; layer.anchorPoint = CGPointZero; [self.view.layer addSublayer:layer]; self.layer = layer; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ // 2秒后,运行隐式动画 // self.layer.backgroundColor = [UIColor blueColor].CGColor; // 假设不想要隐式动画,可通过开启事务 进行关闭动画 // [CATransaction begin]; // [CATransaction setDisableActions:YES]; // self.layer.position = CGPointMake(100, 100); self.layer.opacity = 0.5; // [CATransaction commit]; // 提交事务 }); } #pragma mark - 连线之CALayer的基本属性 // (边框加的是内边框) - (IBAction)layerBasicAttribute_1 { // 边框宽度(边框加的是内边框) self.purpleView.layer.borderWidth = 10; // 边框颜色 self.purpleView.layer.borderColor = [UIColor greenColor].CGColor; // 圆角 self.purpleView.layer.cornerRadius = 10; // self.purpleView.layer.masksToBounds = YES; // 阴影颜色 self.purpleView.layer.shadowColor = [UIColor blueColor].CGColor; // 阴影偏差 self.purpleView.layer.shadowOffset = CGSizeMake(20, 20); // 阴影不透明度 self.purpleView.layer.shadowOpacity = 0.5; } - (IBAction)layerBasicAttribute_2 { // 边框宽度 // self.nanaImgView.layer.borderWidth = 10; // // 边框颜色 // self.nanaImgView.layer.borderColor = [UIColor greenColor].CGColor; // 圆角 self.nanaImgView.layer.cornerRadius = 10; // 超出主层边框范围的内容都剪掉 self.nanaImgView.layer.masksToBounds = YES; // 阴影颜色 self.nanaImgView.layer.shadowColor = [UIColor blueColor].CGColor; // 阴影偏差 self.nanaImgView.layer.shadowOffset = CGSizeMake(20, 20); // 阴影不透明度 self.nanaImgView.layer.shadowOpacity = 0.5; } - (IBAction)layerBasicAttribute_3 { self.nanaImgView.layer.transform = CATransform3DMakeScale(1.5, 0.5, 0); self.nanaImgView.transform = CGAffineTransformMakeRotation(M_PI_4); self.nanaImgView.layer.transform = CATransform3DMakeRotation(M_PI_4, 0, 0, 1); NSValue *value = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(M_PI_4, 0, 0, 1)]; [self.nanaImgView.layer setValue:value forKeyPath:@"transform"]; [self.nanaImgView.layer setValue:@(M_PI_2) forKeyPath:@"transform.rotation"]; self.nanaImgView.layer.transform = CATransform3DMakeScale(0.5, 2, 0); [self.nanaImgView.layer setValue:[NSValue valueWithCATransform3D:CATransform3DMakeScale(0.5, 2, 0)] forKeyPath:@"transform"]; // 能够传递哪些key path, 在官方文档搜索 "CATransform3D key paths" [self.nanaImgView.layer setValue:@(100) forKeyPath:@"transform.translation.x"]; } - (IBAction)layerBasicAttribute_4 { // 创建一个 用于展示图片的图层 // CALayer *layer = [[CALayer alloc] init]; CALayer *layer = [CALayer layer]; layer.backgroundColor = [UIColor redColor].CGColor; layer.bounds = CGRectMake(0, 0, 150, 150); layer.position = CGPointMake(200, 100); layer.cornerRadius = 10; layer.masksToBounds = YES; layer.contents = (id)[UIImage imageNamed:@"nanaLogo.png"].CGImage; [self.view.layer addSublayer:layer]; } @end



    UIView的drawRect画圆
    //
    //  NanaView.m
    //  40_核心动画
    //
    //  Created by beyond on 14-9-19.
    //  Copyright (c) 2014年 com.beyond. All rights reserved.
    /*
     以下是大实话:
     UIView内部有一个根层
     UIView是内部根层的delegate
     1.根层会准备好一个 图层上下文
     2.根层会调用代理(UIView)的drawLayer方法
        而且将1.中准备好的图层上下文传进来
     3.UIView的drawLayer方法,内部,会调用drawRect方法
        因此,经常使用的drawRect方法中的全部东东,
        全部被绘制到了 根层准备好的这个图层上下文中,
     4.所以说,UIView中drawRect方法所画的全部东东,全画到了根层的上下文中
     */
    
    #import "NanaView.h"
    
    @implementation NanaView
    
    // 复习,UIView画圆
    - (void)drawRect:(CGRect)rect
    {
        // 1.取得图形上下文
        CGContextRef ctx =  UIGraphicsGetCurrentContext();
        // 2.设置红色
        CGContextSetRGBFillColor(ctx, 1, 0, 0, 1);
        // 加入圆
        CGContextAddEllipseInRect(ctx, CGRectMake(0, 0, 50, 50));
        // 3.完毕实心绘制
        CGContextFillPath(ctx);
    }
    @end
    


    自己定义图层方式2:自己定义一个子类,继承自CALayer
    //
    //  NanaLayer.m
    //  40_核心动画
    //
    //  Created by beyond on 14-9-19.
    //  Copyright (c) 2014年 com.beyond. All rights reserved.
    /*
     自己定义图层:
     方式1:
        子类,继承CALayer,实现drawLayer:inContext:方法
     方式2:
        在控制器中,直接创建一个子图层,设置其代理为当前控制器,
        让代理实现的drawLayer方法中 画东东
     注意:不管哪一种,都要手动调用子图层的setNeedsDisplay方法
     */
    
    #import "NanaLayer.h"
    
    @implementation NanaLayer
    
    /**
     *  仅仅有明显地调用子图层的setNeedsDisplay方法,
        才会调用drawInContext:方法进行绘制
     */
    - (void)drawInContext:(CGContextRef)ctx
    {
        // 红色
        CGContextSetRGBFillColor(ctx, 1, 0, 0, 1);
        // 加入圆
        CGContextAddEllipseInRect(ctx, CGRectMake(0, 0, 50, 50));
        // 实心绘制
        CGContextFillPath(ctx);
    }
    
    @end
    


    手动创建的CALayer都有着隐式动画,仅仅要改变其值,就会产生动画

    假设不想看到隐式的动画,可通过【事务】关闭

    为图层CALayer加入核心动画CAAmination的标准步骤:

    黑线表示:继承,红线表示:遵守协议

    CAAnimation父类,基类,抽象类,不可直接使用
    动画完毕时,要想继续保持状态,则必须:
    1.removedOnCompletion为NO
    2.fillMode设置为kCAFillModeForwards

    速度控制函数(CAMediaTimingFunction) 
    1.kCAMediaTimingFunctionLinear(线性):匀速,给你一个相对静态的感觉
    2.kCAMediaTimingFunctionEaseIn(渐进):动画缓慢进入。然后加速离开
    3.kCAMediaTimingFunctionEaseOut(渐出):动画全速进入,然后减速的到达目的地
    4.kCAMediaTimingFunctionEaseInEaseOut(渐进渐出):动画缓慢的进入,中间加速,然后减速的到达目的地。这个是默认的动画行为。
    
    CAAnimation在分类中定义了代理方法
    @interface NSObject (CAAnimationDelegate)
    - (void)animationDidStart:(CAAnimation *)anim;
    - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag;
    @end
    
    fillMode属性值(要想fillMode有效,最好设置removedOnCompletion=NO)
    kCAFillModeRemoved 这个是默认值,也就是说当动画開始前和动画结束后,动画对layer都没有影响,动画结束后,layer会恢复到之前的状态
kCAFillModeForwards 当动画结束后,layer会一直保持着动画最后的状态 
kCAFillModeBackwards 在动画開始前,你仅仅要将动画增加了一个layer,layer便马上进入动画的初始状态并等待动画開始.你能够这样设定測试代码,将一个动画增加一个layer的时候延迟5秒运行.然后就会发如今动画没有開始的时候,仅仅要动画被增加了layer,layer便处于动画初始状态 
kCAFillModeBoth 这个事实上就是上面两个的合成.动画增加后開始之前,layer便处于动画初始状态,动画结束后layer保持动画最后的状
    
    CALayer上动画的暂停和恢复
    // t - active local time   图层的本地时间
    // tp - parent layer time  父图层的时间
    // 父图层和图层本地的时间换算公式
    // t = (tp - beginTime) * speed + timeOffset
    // beginTime = tp - (t - timeOffset)/speed
    #pragma mark 暂停CALayer的动画
    -(void)pauseLayer:(CALayer*)layer
    {
        CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
        layer.speed = 0.0; // 让CALayer的时间停止走动
        layer.timeOffset = pausedTime; // 让CALayer的时间停留在pausedTime这个时刻
    }
    #pragma mark 恢复CALayer的动画
    -(void)resumeLayer:(CALayer*)layer
    {
        CFTimeInterval pausedTime = layer.timeOffset;
        layer.speed = 1.0; // 让CALayer的时间继续行走
        layer.timeOffset = 0.0; // 取消上次记录的停留时刻
        layer.beginTime = 0.0; // 取消上次设置的时间
        
        // 计算暂停的时间(这里用CACurrentMediaTime()-pausedTime也是一样的)
        CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
        // 设置相对于父坐标系的開始时间(往后退timeSincePause)
        layer.beginTime = timeSincePause;
    }
    


    抽象类:属性动画类,CAPropertyAnimation
    它两个子类相当重要:
    CABasicAnimation和CAKeyFrameAnimation

    可用子类:CABasicAnimation,仅仅能fromValue和toValue
    动画完毕时,要想继续保持状态,则必须:
    1.removedOnCompletion为NO
    2.fillMode设置为kCAFillModeForwards

    // 平移动画
    CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"position"];
    anim.duration = 1; // 动画持续1秒
    // 由于CGPoint是结构体。所以用NSValue包装成一个OC对象
    anim.fromValue = [NSValue valueWithCGPoint:CGPointMake(50, 50)];
    anim.toValue = [NSValue valueWithCGPoint:CGPointMake(100, 100)];
    [layer addAnimation:anim forKey:@"MyAnim"];
    // 通过MyAnim能够取回对应的动画对象,比方用来中途取消动画
    
    // 缩放动画
    CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"transform"];
    // 没有设置fromValue说明当前状态作为初始值
    // 宽度(width)变为原来的2倍,高度(height)变为原来的1.5倍
    anim.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(2, 1.5, 1)];
    anim.duration = 1;
    [layer addAnimation:anim forKey:nil];
    
    // 旋转动画
    CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"transform"];
    // 这里是以向量(1, 1, 0)为轴,旋转π/2弧度(90°)
    // 假设仅仅是在手机平面上旋转,就设置向量为(0, 0, 1)。即Z轴
    anim.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(M_PI_2, 1, 1, 0)];
    anim.duration = 1;
    [layer addAnimation:anim forKey:nil];
    


    可用子类:CAKeyFrameAnimation,能够设置多个关键点

    在关键帧动画中另一个很重要的參数,那便是calculationMode,计算模式.

    其主要针对的是每一帧的内容为一个坐标点的情况,也就是对anchorPointposition 进行的动画.

    当在平面座标系中有多个离散的点的时候,能够是离散的,也能够直线相连后进行插值计算,也能够使用圆滑的曲线将他们相连后进行插值计算.

    calculationMode眼下提供例如以下几种模式:

    kCAAnimationLinear calculationMode的默认值,表示当关键帧为座标点的时候,关键帧之间直接直线相连进行插值计算;

    kCAAnimationDiscrete 离散的,就是不进行插值计算,全部关键帧直接逐个进行显示;

    kCAAnimationPaced 使得动画均匀进行,而不是按keyTimes设置的或者按关键帧平分时间,此时keyTimes和timingFunctions无效;

    kCAAnimationCubic 对关键帧为座标点的关键帧进行圆滑曲线相连后插值计算,这里的主要目的是使得执行的轨迹变得圆滑

    kCAAnimationCubicPaced 看这个名字就知道和kCAAnimationCubic有一定联系,

    事实上就是在kCAAnimationCubic的基础上使得动画执行变得均匀,就是系统时间内运动的距离同样,

    此时keyTimes以及timingFunctions也是无效的

    动画组CAAnimationGroup,同一时候并发执行组中动画
    动画组是CAAnimation的直接子类,
    因此,它是【CAPropertyAnimation属性动画】的兄弟
    它也是【CATransition转场动画】的兄弟
    【转场动画CATransition】是CAAniamtion的直接子类
    因此,它是【CAPropertyAnimation属性动画】的兄弟
    它也是【CAAnimationGroup动画组】的兄弟
    <pre name="code" class="objc"><div class="cnblogs_code" style="margin:5px 0px; padding:5px; border:1px solid rgb(204,204,204); overflow:auto; line-height:26px; font-family:'Courier New'; background-color:rgb(245,245,245)">
    	<p>
    	</p>
    	<pre name="code" class="objc">/* 转场动画的过渡效果 
    fade     
    //交叉淡化过渡(不支持过渡方向) 
    kCATransitionFade 
    push     
    //新视图把旧视图推出去  
    kCATransitionPush
     moveIn   
    //新视图移到旧视图上面   
    kCATransitionMoveIn 
    reveal   
    //将旧视图移开,显示以下的新视图  
    kCATransitionReveal 
    cube     
    //立方体翻滚效果 
    oglFlip  
    //上下左右翻转效果 
    suckEffect   
    //收缩效果。如一块布被抽走(不支持过渡方向) 
    rippleEffect 
    //滴水效果(不支持过渡方向) 
    pageCurl     
    //向上翻页效果 
    pageUnCurl   
    //向下翻页效果 
    cameraIrisHollowOpen  
    //相机镜头打开效果(不支持过渡方向) 
    cameraIrisHollowClose 
    //相机镜头关上效果(不支持过渡方向)
    */
       
    /* 过渡方向 
    kCATransitionFromRight 
    kCATransitionFromLeft 
    kCATransitionFromBottom 
    kCATransitionFromTop
    */
    
    CATransition的使用
    CATransition *anim = [CATransition animation];
    anim.type = @“cube”; 
    // 动画过渡类型
    anim.subtype = kCATransitionFromTop; 
    // 动画过渡方向
    anim.duration = 1; 
    // 动画持续1s
    // 代理。动画运行完成后会调用delegate的animationDidStop:finished:
    anim.delegate = self;
     
    /*******中间穿插改变layer属性的代码**********/   
    
    [layer addAnimation:anim forKey:nil];
    
    
    


    
    
    核心动画之可动画的属性列表:
     

    一、Core Animation简单介绍

    * Core Animation能够跨Mac OS X和iOS平台。

    * Core Animation的动画运行过程都是在后台操作的,不会堵塞主线程。

    * 要注意的是,Core Animation是直接作用在CALayer上的,并不是UIView,称虚假动画。

     

    二、Core Animation的使用步骤

    1.初始化一个CAAnimation对象。并设置一些动画相关属性

    2.通过调用CALayer的addAnimation:forKey:方法添加CAAnimation对象到CALayer中。这样就能開始运行动画了

    3.通过调用CALayer的removeAnimationForKey:方法能够停止CALayer中的动画

     

    三、CAAnimation

    * 从前面的叙述能够看出。要想运行动画,就必须初始化一个CAAnimation对象。

    * 事实上,普通情况下。我们使用的比較多的是CAAnimation的子类。因此,先大致看看CAAnimation的继承结构:

    黑线代表继承,黑色文字代表类名。

    白色文字代表属性。

    当中CAMediaTiming是一个协议(protocol)。

    由下图看出:CAAnimation有三个儿子:动画组、属性动画、转场动画

    当中属性动画也有二个儿子:基本动画、帧动画

    基本动画仅仅有fromValue和toValue,而帧动画是数组形式

    1.CAAnimation的经常使用属性

    * CAAnimation是全部动画类的父类,可是它不能直接使用,应该使用它的子类

    * 常见属性有:

    1> duration:动画的持续时间

    2> repeatCount:动画的反复次数

    3> timingFunction:控制动画执行的节奏

    timingFunction可选的值有:

    • kCAMediaTimingFunctionLinear(线性):匀速,给你一个相对静态的感觉
    • kCAMediaTimingFunctionEaseIn(渐进):动画缓慢进入,然后加速离开
    • kCAMediaTimingFunctionEaseOut(渐出):动画全速进入,然后减速的到达目的地
    • kCAMediaTimingFunctionEaseInEaseOut(渐进渐出):动画缓慢的进入,中间加速,然后减速的到达目的地。

      这个是默认的动画行为。

    4> delegate:动画代理,用来监听动画的运行过程

    代理对象须要实现的方法有:(这几个方法被定义在NSObject某个分类中)

    1@interface NSObject (CAAnimationDelegate)

    2// 动画開始运行的时候触发这种方法

    3 - (void)animationDidStart:(CAAnimation *)anim;

    4 

    5// 动画运行完成的时候触发这种方法

    6 - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag;

    7@end

    * 上面介绍的全部属性都是属于CAAnimation的。因此。CAAnimation的全部子类都能使用它们。

     

    2.其它

    * CAPropertyAnimation也是不能直接使用的,也要使用它的子类

    * 所以,能用的动画类仅仅剩下4个:

    孙子两个:CABasicAnimation、CAKeyframeAnimation、

    儿子两个:CATransition、CAAnimationGroup

     

    四、CAPropertyAnimation

    * CAPropertyAnimation是CAAnimation的子类,可是不能直接使用,要想创建动画对象,应该使用它的两个子类:CABasicAnimation和CAKeyframeAnimation

    * 它有个NSString类型的keyPath属性。你能够指定CALayer的某个属性名为keyPath,而且对CALayer的这个属性的值进行改动,达到对应的动画效果。比方,指定@"position"为keyPath。就会改动CALayer的position属性的值,以达到平移的动画效果

    * 因此。初始化好CAPropertyAnimation的子类对象后,必须先设置keyPath,搞清楚要改动的是CALayer的哪个属性,运行的是如何的动画

    CABasicAnimation是CAPropertyAnimation的子类。

    使用它能够实现一些主要的动画效果。它能够让CALayer的某个属性从某个值渐变到还有一个值。

    以下就用CABasicAnimation实现几个简单的动画。

    * 先初始化一个UIView加入到控制器的view中。

    然后在这个UIView的layer上运行动画,以下的self是指控制器

    1 _myView = [[UIView alloc] init];

    2 _myView.layer.position = CGPointMake(100,100);

    3 _myView.layer.bounds = CGRectMake(0,0, 100, 100);

    4 _myView.backgroundColor = [UIColor blueColor];

    5[self.view addSubview:_myView];

    6 [_myView release];

    一、平移动画

    1.方法1

     1// 说明这个动画对象要对CALayer的position属性运行动画

     2 CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"position"];

     3// 动画持续1.5s

     4 anim.duration =1.5

     5 

     6// position属性值从(50, 80)渐变到(300, 350)

     7 anim.fromValue = [NSValue valueWithCGPoint:CGPointMake(50,80)];

     8 anim.toValue = [NSValue valueWithCGPoint:CGPointMake(300,350)];

     9 

    10// 设置动画的代理

    11 anim.delegate = self;

    12 

    13// 因为核心动画是虚假动画,所以要显式指出 保持动画运行后的状态

    14 anim.removedOnCompletion = NO;

    15 anim.fillMode = kCAFillModeForwards;

    16 

    17// 加入动画对象到图层上

    18 [_myView.layer addAnimation:anim forKey:@"translate"];

    * 第2行设置的keyPath是@"position",说明要改动的是CALayer的position属性,也就是会运行平移动画

    * 注意第7、8行,这里并非直接使用CGPoint这样的结构体类型。而是要先包装成NSValue对象后再使用。这2行代码表示CALayer从位置(50, 80)移动到位置(300, 350)

    * 假设将第8行的toValue换成byValue,代表CALayer从位置(50, 80)開始向右移动300、向下移动350,也就是移动到位置(350, 430)

    * 默认情况下,动画运行完成后,动画会自己主动从CALayer上移除,CALayer又会回到原来的状态。为了保持动画运行后的状态,能够增加第14、15行代码

    * 第18行后面的@"translate"是给动画对象起个名称,以后能够调用CALayer的removeAnimationForKey:方法依据动画名称停止对应的动画

    * 第11行是设置动画的代理,能够监听动画的运行过程,这里设置控制器为代理。代理须要实现的方法有:

     1#pragma mark 动画開始

     2 - (void)animationDidStart:(CAAnimation *)anim {

     3     NSLog(@"动画開始了");

     4 }

     5 

     6#pragma mark 动画结束

     7 - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {

     8    // 查看一下动画运行完成后的position值

     9     NSString *string = NSStringFromCGPoint(_myView.layer.position);

    10     NSLog(@"动画结束了。position:%@",string);

    11 }

    2.方法2

    1 CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"transform"];

    2 anim.duration =1;

    3 // 參数是:X、Y、Z

    4CATransform3D form =CATransform3DMakeTranslation(350,350,0);

    5 anim.toValue = [NSValue valueWithCATransform3D:form];

    6 

    7 [_myView.layer addAnimation:anim forKey:nil];

    通过CALayer的transform属性实现平移动画。layer会从自己的初始位置平移到(350, 350)位置

     

    二、缩放动画

    1.方法1

    1 CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"bounds"];

    2 anim.duration =2;

    3 

    4 anim.toValue = [NSValue valueWithCGRect:CGRectMake(0,0, 30, 30)];

    5 

    6 [_myView.layer addAnimation:anim forKey:nil];

    layer会从原来的尺寸(100x100)变为30x30

     

    2.方法2

    1 CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"transform"];

    2 anim.duration =1.5; // 动画持续1.5s

    3 // 參数:X、Y、Z

    4// CALayer的宽度从0.5倍变为2倍

    5// CALayer的高度从0.5倍变为1.5倍

    6 anim.fromValue = [NSValuevalueWithCATransform3D:CATransform3DMakeScale(0.5,0.5,1)];

    7 anim.toValue  = [NSValuevalueWithCATransform3D:CATransform3DMakeScale(2,1.5,1)];

    8 

    9 [_myView.layer addAnimation:anim forKey:nil];

     

    三、旋转动画

    1 CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"transform"];

    2 anim.duration =1.5;

    3 // 參数:X、Y、Z 

    4// 绕着(0, 0, 1)这个向量轴顺时针旋转45°

    5 anim.toValue = [NSValuevalueWithCATransform3D:CATransform3DMakeRotation(M_PI_4,0,0,1)];

    6 

    7 [_myView.layer addAnimation:anim forKey:nil]; 

    事实上能够不用设置fromValue,这里仅仅设置了toValue

     

    四、其它

    * 除开前面使用的position、transform属性,事实上CALayer还有好多属性都能够形成动画,这些属性统称为"Animatable Properties"。

    * CABasicAnimation尽管可以做非常多主要的动画效果。

    可是有个局限性。只能让CALayer的属性从某个值渐变到还有一个值。不过在2个值之间渐变,

    因此:CAKeyFrameAnimation能够看成是扩展了的CABasicAnimation

    UIView自带的动画实现功能
    // 说明须要运行动画
    [UIView beginAnimations:nil context:nil];
    // 设置动画持续事件
    [UIView setAnimationDuration:1];
    // 设置转场动画
    [UIView setAnimationTransition:UIViewAnimationTransitionCurlUp forView:self.view cache:YES];
    
    // 交换子视图的位置
    [self.view exchangeSubviewAtIndex:0 withSubviewAtIndex:1];
    
    // 提交动画
    [UIView commitAnimations];
    



    •动画的节奏控制,跟CAAnimation的timingFunction属性类似

    typedefNS_ENUM(NSInteger, UIViewAnimationCurve) {

        UIViewAnimationCurveEaseInOut,         // slow at beginning and end

        UIViewAnimationCurveEaseIn,            // slow at beginning

        UIViewAnimationCurveEaseOut,           // slow at end

        UIViewAnimationCurveLinear

    };



    typedef NS_ENUM(NSInteger,UIViewAnimationTransition) {

        UIViewAnimationTransitionNone,

        UIViewAnimationTransitionFlipFromLeft,

        UIViewAnimationTransitionFlipFromRight,

        UIViewAnimationTransitionCurlUp,

        UIViewAnimationTransitionCurlDown,

    };


    使用Block包装动画

    •动画的节奏控制枚举常量

    UIViewAnimationOptionCurveEaseInOut

    UIViewAnimationOptionCurveEaseIn              

    UIViewAnimationOptionCurveEaseOut

    UIViewAnimationOptionCurveLinear 



    •转场动画的类型

    UIViewAnimationOptionTransitionNone

    UIViewAnimationOptionTransitionFlipFromLeft

    UIViewAnimationOptionTransitionFlipFromRight  

    UIViewAnimationOptionTransitionCurlUp

    UIViewAnimationOptionTransitionCurlDown

    UIViewAnimationOptionTransitionCrossDissolve

    UIViewAnimationOptionTransitionFlipFromTop

    UIViewAnimationOptionTransitionFlipFromBottom



    UIImageView封装的序列帧动画

    UIActivityIndicatorView指示器
    操作处理中,给用户友好体验













    
    
    
    
  • 相关阅读:
    python判断字典中key是否存在
    获取redis中所有的key,清空整个 Redis 服务器的数据
    python redis模块详解
    Windows中redis的下载及安装、设置
    .htaccess
    python介绍
    vi和vim
    其他
    Linux系统相关命令
    Linux用户权限常见命令
  • 原文地址:https://www.cnblogs.com/llguanli/p/6869327.html
Copyright © 2020-2023  润新知