iOS_40_核心动画
//
// 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.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.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
//
// 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
//
// 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
#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");
}
// 角度转弧度公式,角*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];
}
//
// 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
//
// 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
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcHJlX2VtaW5lbnQ=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcHJlX2VtaW5lbnQ=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">
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内部的根层也是父子层的关系
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcHJlX2VtaW5lbnQ=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">
自己定义图层。事实上就是在层上画图,一共同拥有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中,最后才被拷贝至屏幕,进行显示
//
// 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
//
// 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
//
// 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
速度控制函数(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;
}
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcHJlX2VtaW5lbnQ=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">
// 平移动画
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];
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcHJlX2VtaW5lbnQ=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">
在关键帧动画中另一个很重要的參数,那便是calculationMode,计算模式.
其主要针对的是每一帧的内容为一个坐标点的情况,也就是对anchorPoint 和position
进行的动画.
当在平面座标系中有多个离散的点的时候,能够是离散的,也能够直线相连后进行插值计算,也能够使用圆滑的曲线将他们相连后进行插值计算.
calculationMode眼下提供例如以下几种模式:
kCAAnimationLinear calculationMode的默认值,表示当关键帧为座标点的时候,关键帧之间直接直线相连进行插值计算;
kCAAnimationDiscrete 离散的,就是不进行插值计算,全部关键帧直接逐个进行显示;
kCAAnimationPaced 使得动画均匀进行,而不是按keyTimes设置的或者按关键帧平分时间,此时keyTimes和timingFunctions无效;
kCAAnimationCubic 对关键帧为座标点的关键帧进行圆滑曲线相连后插值计算,这里的主要目的是使得执行的轨迹变得圆滑;
kCAAnimationCubicPaced 看这个名字就知道和kCAAnimationCubic有一定联系,
事实上就是在kCAAnimationCubic的基础上使得动画执行变得均匀,就是系统时间内运动的距离同样,
此时keyTimes以及timingFunctions也是无效的
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcHJlX2VtaW5lbnQ=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">
<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,而帧动画是数组形式
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcHJlX2VtaW5lbnQ=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">
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 beginAnimations:nil context:nil];
// 设置动画持续事件
[UIView setAnimationDuration:1];
// 设置转场动画
[UIView setAnimationTransition:UIViewAnimationTransitionCurlUp forView:self.view cache:YES]; // 交换子视图的位置
[self.view exchangeSubviewAtIndex:0 withSubviewAtIndex:1]; // 提交动画
[UIView commitAnimations];
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcHJlX2VtaW5lbnQ=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">
typedefNS_ENUM(NSInteger, UIViewAnimationCurve) {
UIViewAnimationCurveEaseInOut, // slow at beginning and end
UIViewAnimationCurveEaseIn, // slow at beginning
UIViewAnimationCurveEaseOut, // slow at end
UIViewAnimationCurveLinear
};
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcHJlX2VtaW5lbnQ=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">
typedef NS_ENUM(NSInteger,UIViewAnimationTransition)
{
UIViewAnimationTransitionNone,
UIViewAnimationTransitionFlipFromLeft,
UIViewAnimationTransitionFlipFromRight,
UIViewAnimationTransitionCurlUp,
UIViewAnimationTransitionCurlDown,
};
UIViewAnimationOptionCurveEaseInOut
UIViewAnimationOptionCurveEaseIn
UIViewAnimationOptionCurveEaseOut
UIViewAnimationOptionCurveLinear
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcHJlX2VtaW5lbnQ=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">
UIViewAnimationOptionTransitionNone
UIViewAnimationOptionTransitionFlipFromLeft
UIViewAnimationOptionTransitionFlipFromRight
UIViewAnimationOptionTransitionCurlUp
UIViewAnimationOptionTransitionCurlDown
UIViewAnimationOptionTransitionCrossDissolve
UIViewAnimationOptionTransitionFlipFromTop
UIViewAnimationOptionTransitionFlipFromBottom
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcHJlX2VtaW5lbnQ=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">
iOS_40_核心动画的更多相关文章
- 核心动画 - CAKeyframeAnimation 简单应用
核心动画: 登录按钮的抖动效果: CAKeyframeAnimation * kfAnimation = [CAKeyframeAnimation animationWithKeyPath:@&quo ...
- iOS开发UI篇—核心动画(UIView封装动画)
iOS开发UI篇—核心动画(UIView封装动画) 一.UIView动画(首尾) 1.简单说明 UIKit直接将动画集成到UIView类中,当内部的一些属性发生改变时,UIView将为这些改变提供动画 ...
- iOS开发UI篇—核心动画(转场动画和组动画)
转自:http://www.cnblogs.com/wendingding/p/3801454.html iOS开发UI篇—核心动画(转场动画和组动画) 一.转场动画简单介绍 CAAnimation的 ...
- iOS开发UI篇—核心动画(关键帧动画)
转自:http://www.cnblogs.com/wendingding/p/3801330.html iOS开发UI篇—核心动画(关键帧动画) 一.简单介绍 是CApropertyAnimatio ...
- iOS开发UI篇—核心动画(基础动画)
转自:http://www.cnblogs.com/wendingding/p/3801157.html 文顶顶 最怕你一生碌碌无为 还安慰自己平凡可贵 iOS开发UI篇—核心动画(基础动画) iOS ...
- iOS开发UI篇—核心动画简介
转自:http://www.cnblogs.com/wendingding/p/3801036.html iOS开发UI篇—核心动画简介 一.简单介绍 Core Animation,中文翻译为核心动画 ...
- 核心动画与UIView的区别
核心动画与UIView的区别 1.核心动画只作用于layer,使用核心动画之前,必须有layer 2.核心动画只是假象,并没有移动实际位置 什么时候使用核心动画,什么时候使用UIView动画 1.当不 ...
- 核心动画 (CAAnimationGroup)
Main.storyboard ViewController.m // // ViewController.m // 8A05.核心动画 CAAnimationGroup // // Creat ...
- 核心动画(CAKeyframeAnimation)
Main.storyboard ViewController.m // // ViewController.m // 8A02.核心动画 - CAKeyframeAnimation // // ...
随机推荐
- BZOJ 4557: [JLoi2016]侦察守卫
题目大意:每个点有一个放置守卫的代价,同时每个点放置守卫能覆盖到的距离都为d,问覆盖所有给定点的代价是多少. 题解: 树形DP f[x][y]表示x子树中所有点都已经覆盖完,并且x还能向上覆盖y层的最 ...
- Scrapy应用之抓取《宦海沉浮》小说
目标站点 http://www.shushu8.com/huanhaichenfu/ 第一步:新建项目 KeysdeMacBook:Desktop keys$ scrapy startprojec ...
- 牛客网Wannafly模拟赛
A矩阵 时间限制:1秒 空间限制:131072K 题目描述 给出一个n * m的矩阵.让你从中发现一个最大的正方形.使得这样子的正方形在矩阵中出现了至少两次.输出最大正方形的边长. 输入描述: 第一行 ...
- 持续集成---jenkins环境部署
一.环境准备 操作系统:linux系统,此时我安装的是centos6.5,操作步骤具体见博客<虚拟机安装centos6.5> 依赖软件:1.jdk, 2.tomcat9(需要安装两个,一个 ...
- 【Luogu】P3708Koishi的数字游戏(数论)
题目链接 考虑f(i)=i%1+i%2+i%3+.....+i%n f(i+1)=(i+1)%1+(i+1)%2+......+(i+1)%n 其中不是i+1的因数的部分在f(i+1)的地方都加了1. ...
- BZOJ 2440 [中山市选2011]完全平方数 ——莫比乌斯函数
$\sum_{i=1}^n[i==d^2*p]$ 其中p无平方因子$=\sum_{d^2\mid n,d>=2}\sum_{i=1}^{\lfloor {n/d^2} \rfloor} \lef ...
- 如何解决"The superclass "javax.servlet.http.HttpServlet" was not found on the Java Build Path"
今天我在eclipse上搭建新项目时,莫名其妙的出现这个错误,如下: The superclass "javax.servlet.http.HttpServlet" was not ...
- angularjs bootstrap 简介
1.angular特色 数据双向绑定 模块化 var myapp=angular.module... 控制器 myapp.controller(){} 2.bootstrap特色 栅格结构 cs ...
- ftp链接、上传、下载、断开
开发环境:Jdk 1.8 引入第三方库:commons-net-2.2.jar(针对第一种方法) 一.基于第三方库FtpClient的FTP服务器数据传输 由于是基于第三方库,所以这里基本上没有太多要 ...
- Laravel 5.1的多路由文件的配置
Laravel 5.1的多路由文件的配置 默认的路由配置文件只有一个,\app\Http\routes.php.在同一个文件中写路由容易起冲突,文件会越来越大,就需要定义多个路由文件.找到加载\app ...