iOS_40_核心动画

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

核心动画之CAKeyFrameAnimation,图标抖动效果
最终效果图:
iOS_40_核心动画_第1张图片
核心动画之CAAnimationGroup(动画组)
最终效果图:
iOS_40_核心动画_第2张图片
核心动画之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;
// 三步曲,切换按钮的选中状态
@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个按钮
// 在这个方法中,取得属性才是有值的
- (void)awakeFromNib
{
    // 让imageView能跟用户进行交互
    self.centerCircle.userInteractionEnabled = YES;
    // 一次性添加12个按钮
    [self add12Btns];
}
#define kAngleToRadius(angle) (angle)/180.0*M_PI
// 一次性添加12个按钮
- (void)add12Btns
{
    // 一次性 添加12个按钮
    for (int i = 0; i < 12; i++) {
        // 1.创建一个按钮
        CircleBtn *btn = [CircleBtn buttonWithType:UIButtonTypeCustom];
        // 2.设置按钮的基本属性,并且根据i的不同,设置小按钮的旋转角度
        [self setBtnBasicAttribute:btn index:i];
        // 3.裁剪大图,并根据i的不同,从大图中不同位置rect,截取一个小图片
        [self setBtnBg:btn bigImg:@"LuckyAstrology" selectedBigImg:@"LuckyAstrologyPressed" index:i];
        // 4.监听按钮点击
        [btn addTarget:self action:@selector(btnClick:) forControlEvents:UIControlEventTouchDown];
        // 5.添加到self.view
        [self.centerCircle addSubview:btn];
        // 6.默认选中第0个按钮
        if (i == 0) {
            [self btnClick:btn];
        }
    }
}
// 抽取的方法,基本属性,并且根据i的不同,设置小按钮的旋转角度
- (void)setBtnBasicAttribute:(UIButton *)btn index:(int)i
{
    btn.bounds = CGRectMake(0, 0, 68, 143);
    // 设置按钮选中时的背景图片(红红的)
    [btn setBackgroundImage:[UIImage imageNamed:@"LuckyRototeSelected"] forState:UIControlStateSelected];
    // 为了所有的12个小按钮,能够排成一个圆形,位置相同,锚点为底边的中点,根据i不同,绕着锚点进行旋转
    // 位置全部相同,居中心
    btn.layer.position = CGPointMake(self.centerCircle.frame.size.width * 0.5, self.centerCircle.frame.size.height * 0.5);
    // 设置按钮图层的锚点(底边的中点)
    btn.layer.anchorPoint = CGPointMake(0.5, 1);
    // 设置按钮的旋转角度(根据i不同,绕着锚点进行旋转)
    // 角度转弧度 kAngleToRadius(angle) angle/180.0*M_PI
    // CGFloat angle = (30 * i) / 180.0 * M_PI;
    CGFloat angle =  kAngleToRadius(30*i);
    // 设置按钮的旋转角度
    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.设置按钮的默认和选中时的小图片
    [btn setImage:[UIImage imageWithCGImage:smallImg] forState:UIControlStateNormal];
    [btn setImage:[UIImage imageWithCGImage:smallImgSelected] forState:UIControlStateSelected];
}

// 监听按钮点击,三步曲,切换按钮的选中状态
- (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


iOS_40_核心动画_第3张图片
自定义CircleBtn
//
//  CircleBtn.m
//  40_核心动画
//
//  Created by beyond on 14-9-19.
//  Copyright (c) 2014年 com.beyond. All rights reserved.
//

#import "CircleBtn.h"

@implementation CircleBtn

// 调整按钮中的图片的位置
- (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之间的关系
iOS_40_核心动画_第4张图片
UIView本身不可视,其内部的CALayer才具有显示功能
iOS_40_核心动画_第5张图片
改变CALayer的属性,就是改变UIView的外观(如圆角、阴影)
iOS_40_核心动画_第6张图片
CALayer的常用属性(如位置、锚点)
iOS_40_核心动画_第7张图片
CALayer的常用属性(如边框、圆角)
iOS_40_核心动画_第8张图片
UIColor和UIImage必须转成可以跨平台的CGColorRef、CGImageRef

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

锚点就是处在父层的position坐标处的那个点
iOS_40_核心动画_第9张图片
红色图层的锚点为(0,0)时的情况

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

红色图层的锚点为(1,0.5)时的情况
iOS_40_核心动画_第10张图片
红色图层的锚点为(0.5,0.5)时的情况(默认值)
iOS_40_核心动画_第11张图片
创建子图层

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内部的根层也是父子层的关系

iOS_40_核心动画_第12张图片

重要~自定义图层

自定义图层,其实就是在层上绘图,一共有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都有着隐式动画,只要改变其值,就会产生动画

如果不想看到隐式的动画,可通过【事务】关闭
iOS_40_核心动画_第13张图片
为图层CALayer添加核心动画CAAmination的标准步骤:
iOS_40_核心动画_第14张图片
黑线表示:继承,红线表示:遵守协议
iOS_40_核心动画_第15张图片
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
iOS_40_核心动画_第16张图片
可用子类:CABasicAnimation,只能fromValue和toValue
动画完成时,要想继续保持状态,则必须:
1.removedOnCompletion为NO
2.fillMode设置为kCAFillModeForwards
iOS_40_核心动画_第17张图片
// 平移动画
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,可以设置多个关键点
iOS_40_核心动画_第18张图片

在关键帧动画中还有一个非常重要的参数,那便是calculationMode,计算模式.

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

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

calculationMode目前提供如下几种模式:

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

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

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

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

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

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

此时keyTimes以及timingFunctions也是无效的

动画组CAAnimationGroup,同时并发运行组中动画
动画组是CAAnimation的直接子类,
因此,它是【CAPropertyAnimation属性动画】的兄弟
它也是【CATransition转场动画】的兄弟
iOS_40_核心动画_第19张图片
【转场动画CATransition】是CAAniamtion的直接子类
因此,它是【CAPropertyAnimation属性动画】的兄弟
它也是【CAAnimationGroup动画组】的兄弟
iOS_40_核心动画_第20张图片
<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];




 
 
核心动画之可动画的属性列表:
iOS_40_核心动画_第21张图片 

一、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,而帧动画是数组形式

iOS_40_核心动画_第22张图片

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自带的动画实现功能
iOS_40_核心动画_第23张图片
// 说明需要执行动画
[UIView beginAnimations:nil context:nil];
// 设置动画持续事件
[UIView setAnimationDuration:1];
// 设置转场动画
[UIView setAnimationTransition:UIViewAnimationTransitionCurlUp forView:self.view cache:YES];

// 交换子视图的位置
[self.view exchangeSubviewAtIndex:0 withSubviewAtIndex:1];

// 提交动画
[UIView commitAnimations];


iOS_40_核心动画_第24张图片
•动画的节奏控制,跟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包装动画
iOS_40_核心动画_第25张图片
•动画的节奏控制枚举常量

UIViewAnimationOptionCurveEaseInOut

UIViewAnimationOptionCurveEaseIn              

UIViewAnimationOptionCurveEaseOut

UIViewAnimationOptionCurveLinear 


iOS_40_核心动画_第26张图片
•转场动画的类型

UIViewAnimationOptionTransitionNone

UIViewAnimationOptionTransitionFlipFromLeft

UIViewAnimationOptionTransitionFlipFromRight  

UIViewAnimationOptionTransitionCurlUp

UIViewAnimationOptionTransitionCurlDown

UIViewAnimationOptionTransitionCrossDissolve

UIViewAnimationOptionTransitionFlipFromTop

UIViewAnimationOptionTransitionFlipFromBottom



UIImageView封装的序列帧动画

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













 
 
 
 

你可能感兴趣的:(ios,动画,CAlayer,绘图,QuartzCore)