自定义 Layer 属性的动画
默认情况下,CALayer 及其子类的绝大部分标准属性都可以执行动画,无论是添加一个 CAAnimation 到 Layer(显式动画),亦或是为属性指定一个动作然后修改它(隐式动画)。
- @interface ClockFace: CAShapeLayer
- @property (nonatomic, strong) NSDate *time;
- @end
- @interface ClockFace ()
- // 私有属性,译者注:这里申明的是 CALayer ,下面分配的却是 CAShapeLayer ,按照文字,应该都是 CAShapeLayer 才对
- @property (nonatomic, strong) CALayer *hourHand;
- @property (nonatomic, strong) CALayer *minuteHand;
- @end
- @implementation ClockFace
- - (id)init
- {
- if ((self = [super init]))
- {
- self.bounds = CGRectMake(0, 0, 200, 200);
- self.path = [UIBezierPath bezierPathWithOvalInRect:self.bounds].CGPath;
- self.fillColor = [UIColor whiteColor].CGColor;
- self.strokeColor = [UIColor blackColor].CGColor;
- self.lineWidth = 4;
- self.hourHand = [CAShapeLayer layer];
- self.hourHand.path = [UIBezierPath bezierPathWithRect:CGRectMake(-2, -70, 4, 70)].CGPath;
- self.hourHand.fillColor = [UIColor blackColor].CGColor;
- self.hourHand.position = CGPointMake(self.bounds.size.width / 2, self.bounds.size.height / 2);
- [self addSublayer:self.hourHand];
- self.minuteHand = [CAShapeLayer layer];
- self.minuteHand.path = [UIBezierPath bezierPathWithRect:CGRectMake(-1, -90, 2, 90)].CGPath;
- self.minuteHand.fillColor = [UIColor blackColor].CGColor;
- self.minuteHand.position = CGPointMake(self.bounds.size.width / 2, self.bounds.size.height / 2);
- [self addSublayer:self.minuteHand];
- }
- return self;
- }
- @end
- @interface ViewController ()
- @property (nonatomic, strong) IBOutlet UIDatePicker *datePicker;
- @property (nonatomic, strong) ClockFace *clockFace;
- @end
- @implementation ViewController
- - (void)viewDidLoad
- {
- [super viewDidLoad];
- // 添加时钟面板 Layer
- self.clockFace = [[ClockFace alloc] init];
- self.clockFace.position = CGPointMake(self.view.bounds.size.width / 2, 150);
- [self.view.layer addSublayer:self.clockFace];
- // 设置默认时间
- self.clockFace.time = [NSDate date];
- }
- - (IBAction)setTime
- {
- self.clockFace.time = self.datePicker.date;
- }
- @end
- - (void)setTime:(NSDate *)time
- {
- _time = time;
- NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
- NSDateComponents *components = [calendar components:NSHourCalendarUnit | NSMinuteCalendarUnit fromDate:time];
- self.hourHand.affineTransform = CGAffineTransformMakeRotation(components.hour / 12.0 * 2.0 * M_PI);
- self.minuteHand.affineTransform = CGAffineTransformMakeRotation(components.minute / 60.0 * 2.0 * M_PI);
- }
![](http://www.cocoachina.com/cms/uploads/allimg/140519/8369_140519134119_1.gif)
- @interface ClockFace ()
- @end
- @implementation ClockFace
- @dynamic time;
- - (id)init
- {
- if ((self = [super init]))
- {
- self.bounds = CGRectMake(0, 0, 200, 200);
- }
- return self;
- }
- @end
- @interface ViewController () <UITextFieldDelegate>
- @property (nonatomic, strong) IBOutlet UITextField *textField;
- @property (nonatomic, strong) ClockFace *clockFace;
- @end
- @implementation ViewController
- - (void)viewDidLoad
- {
- [super viewDidLoad];
- // 添加时钟面板 Layer
- self.clockFace = [[ClockFace alloc] init];
- self.clockFace.position = CGPointMake(self.view.bounds.size.width / 2, 150);
- [self.view.layer addSublayer:self.clockFace];
- }
- - (BOOL)textFieldShouldReturn:(UITextField *)textField
- {
- [textField resignFirstResponder];
- return YES;
- }
- - (void)textFieldDidEndEditing:(UITextField *)textField
- {
- self.clockFace.time = [textField.text floatValue];
- }
- @end
- + (BOOL)needsDisplayForKey:(NSString *)key
- {
- if ([@"time" isEqualToString:key])
- {
- return YES;
- }
- return [super needsDisplayForKey:key];
- }
- - (void)display
- {
- NSLog(@"time: %f", self.time);
- }
- 2014-04-28 22:37:04.253 ClockFace[49145:60b] time: 1.500000
- - (id<CAAction>)actionForKey:(NSString *)key
- {
- if ([key isEqualToString:@"time"])
- {
- CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:key];
- animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
- animation.fromValue = @(self.time);
- return animation;
- }
- return [super actionForKey:key];
- }
- 2014-04-28 22:37:04.253 ClockFace[49145:60b] time: 1.500000
- 2014-04-28 22:37:04.255 ClockFace[49145:60b] time: 1.500000
- 2014-04-28 22:37:04.351 ClockFace[49145:60b] time: 1.500000
- 2014-04-28 22:37:04.370 ClockFace[49145:60b] time: 1.500000
- 2014-04-28 22:37:04.388 ClockFace[49145:60b] time: 1.500000
- 2014-04-28 22:37:04.407 ClockFace[49145:60b] time: 1.500000
- 2014-04-28 22:37:04.425 ClockFace[49145:60b] time: 1.500000
- 2014-04-28 22:37:04.443 ClockFace[49145:60b] time: 1.500000
- 2014-04-28 22:37:04.461 ClockFace[49145:60b] time: 1.500000
- 2014-04-28 22:37:04.479 ClockFace[49145:60b] time: 1.500000
- 2014-04-28 22:37:04.497 ClockFace[49145:60b] time: 1.500000
- 2014-04-28 22:37:04.515 ClockFace[49145:60b] time: 1.500000
- 2014-04-28 22:37:04.755 ClockFace[49145:60b] time: 1.500000
- - (id<CAAction>)actionForKey:(NSString *)key
- {
- if ([key isEqualToString:@"time"])
- {
- CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:key];
- animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
- animation.fromValue = @([[self presentationLayer] time]);
- return animation;
- }
- return [super actionForKey:key];
- }
- - (void)display
- {
- NSLog(@"time: %f", [[self presentationLayer] time]);
- }
- 2014-04-28 22:43:31.200 ClockFace[49176:60b] time: 0.000000
- 2014-04-28 22:43:31.203 ClockFace[49176:60b] time: 0.002894
- 2014-04-28 22:43:31.263 ClockFace[49176:60b] time: 0.363371
- 2014-04-28 22:43:31.300 ClockFace[49176:60b] time: 0.586421
- 2014-04-28 22:43:31.318 ClockFace[49176:60b] time: 0.695179
- 2014-04-28 22:43:31.336 ClockFace[49176:60b] time: 0.803713
- 2014-04-28 22:43:31.354 ClockFace[49176:60b] time: 0.912598
- 2014-04-28 22:43:31.372 ClockFace[49176:60b] time: 1.021573
- 2014-04-28 22:43:31.391 ClockFace[49176:60b] time: 1.134173
- 2014-04-28 22:43:31.409 ClockFace[49176:60b] time: 1.242892
- 2014-04-28 22:43:31.427 ClockFace[49176:60b] time: 1.352016
- 2014-04-28 22:43:31.446 ClockFace[49176:60b] time: 1.460729
- 2014-04-28 22:43:31.464 ClockFace[49176:60b] time: 1.500000
- 2014-04-28 22:43:31.636 ClockFace[49176:60b] time: 1.500000
- - (void)display
- {
- // 获取时间插值
- float time = [self.presentationLayer time];
- // 创建绘制上下文
- UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 0);
- CGContextRef ctx = UIGraphicsGetCurrentContext();
- // 绘制时钟面板
- CGContextSetLineWidth(ctx, 4);
- CGContextStrokeEllipseInRect(ctx, CGRectInset(self.bounds, 2, 2));
- // 绘制时针
- CGFloat angle = time / 12.0 * 2.0 * M_PI;
- CGPoint center = CGPointMake(self.bounds.size.width / 2, self.bounds.size.height / 2);
- CGContextSetLineWidth(ctx, 4);
- CGContextMoveToPoint(ctx, center.x, center.y);
- CGContextAddLineToPoint(ctx, center.x + sin(angle) * 80, center.y - cos(angle) * 80);
- CGContextStrokePath(ctx);
- // 绘制分针
- angle = (time - floor(time)) * 2.0 * M_PI;
- CGContextSetLineWidth(ctx, 2);
- CGContextMoveToPoint(ctx, center.x, center.y);
- CGContextAddLineToPoint(ctx, center.x + sin(angle) * 90, center.y - cos(angle) * 90);
- CGContextStrokePath(ctx);
- //set backing image 设置 contents
- self.contents = (id)UIGraphicsGetImageFromCurrentImageContext().CGImage;
- UIGraphicsEndImageContext();
- }
![](http://www.cocoachina.com/cms/uploads/allimg/140519/8369_140519134434_1.gif)
- const NSInteger hoursOnAClockFace = 12;
- - (void)display
- {
- // 获取时间插值
- float time = [self.presentationLayer time] / hoursOnAClockFace;
- // 从之前定义好的图像数组里获取图像帧
- NSInteger numberOfFrames = [self.frames count];
- NSInteger index = round(time * numberOfFrames) % numberOfFrames;
- UIImage *frame = self.frames[index];
- self.contents = (id)frame.CGImage;
- }
- @interface AudioLayer : CALayer
- - (id)initWithAudioFileURL:(NSURL *)URL;
- @property (nonatomic, assign) float volume;
- - (void)play;
- - (void)stop;
- - (BOOL)isPlaying;
- @end
- @interface AudioLayer ()
- @property (nonatomic, strong) AVAudioPlayer *player;
- @end
- @implementation AudioLayer
- @dynamic volume;
- - (id)initWithAudioFileURL:(NSURL *)URL
- {
- if ((self = [self init]))
- {
- self.volume = 1.0;
- self.player = [[AVAudioPlayer alloc] initWithContentsOfURL:URL error:NULL];
- }
- return self;
- }
- - (void)play
- {
- [self.player play];
- }
- - (void)stop
- {
- [self.player stop];
- }
- - (BOOL)isPlaying
- {
- return self.player.playing;
- }
- + (BOOL)needsDisplayForKey:(NSString *)key
- {
- if ([@"volume" isEqualToString:key])
- {
- return YES;
- }
- return [super needsDisplayForKey:key];
- }
- - (id<CAAction>)actionForKey:(NSString *)key
- {
- if ([key isEqualToString:@"volume"])
- {
- CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:key];
- animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
- animation.fromValue = @([[self presentationLayer] volume]);
- return animation;
- }
- return [super actionForKey:key];
- }
- - (void)display
- {
- // 设置音量值为合适的音量插值
- self.player.volume = [self.presentationLayer volume];
- }
- @end
- @interface ViewController ()
- @property (nonatomic, strong) AudioLayer *audioLayer;
- @end
- @implementation ViewController
- - (void)viewDidLoad
- {
- [super viewDidLoad];
- NSURL *musicURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"music" ofType:@"caf"]];
- self.audioLayer = [[AudioLayer alloc] initWithAudioFileURL:musicURL];
- [self.view.layer addSublayer:self.audioLayer];
- }
- - (IBAction)playPauseMusic:(UIButton *)sender
- {
- if ([self.audioLayer isPlaying])
- {
- [self.audioLayer stop];
- [sender setTitle:@"Play Music" forState:UIControlStateNormal];
- }
- else
- {
- [self.audioLayer play];
- [sender setTitle:@"Pause Music" forState:UIControlStateNormal];
- }
- }
- - (IBAction)fadeIn
- {
- self.audioLayer.volume = 1;
- }
- - (IBAction)fadeOut
- {
- self.audioLayer.volume = 0;
- }
- @end
自定义 Layer 属性的动画相关推荐
- iOS 自定义下拉线条动画
本文摘录自 A GUIDE TO IOS ANIMATION,中文名:<Kitten 的 iOS 动画学习手册>.这是一本非常有趣地介绍 iOS 动画的交互式电子书,提供生动的可交互式元素 ...
- android 自定义 theme,Android使用Theme自定义Activity进入退出动画的方法
本文实例讲述了Android使用Theme自定义Activity进入退出动画的方法.分享给大家供大家参考,具体如下: 有没有觉得Activity的默认动画太快了或者太难看了.. 我原来使用Activi ...
- jQuery 设计和自定义一个带展开动画效果的导航栏
设计和自定义一个带展开动画效果的导航栏,尝试写了一个demo,加上设计和调试差不多写了一天吧. 这里就来讲讲如何从设计->写布局->写样式->写JS代码 完成一个完全自己设计的导航栏 ...
- iOS 自定义layer的两种方式
在iOS中,你能看得见摸得着的东西基本都是UIView,比如一个按钮,一个标签,一个文本输入框,这些都是UIView: 其实UIView之所以能显示在屏幕上,完全是因为它内部的一个图层 在创建UIVi ...
- Toast拓展--自定义显示时间和动画
Toast拓展–自定义显示时间和动画 我们在Android应用开发中经常会需要在界面上弹出一个对界面操作无影响的小提示框来提示用户一些信息,这时候一般都会使用Android原生的Toast类 Toas ...
- iOS 自定义页面的切换动画与交互动画 By Swift
iOS7之前,开发者为了寻求自定义Navigation Controller的Push/Pop动画,只能受限于子类化一个UINavigationController,或是用自定义的动画去覆盖它.但是随 ...
- css自定义盒子形状及动画应用
css自定义盒子形状及动画应用 <style> clip-path: polygon(X1 Y1,X2 Y2,....) </style> 当我们写页面时对盒子的形状有需求时, ...
- android录音波浪动画_Android自定义View实现波浪动画
本文实例为大家分享了Android自定义View实现波浪动画的具体代码,供大家参考,具体内容如下 效果演示 代码调用与实现效果 xml中调用 android:layout_width="ma ...
- Android基础控件——ProgressBar自定义的介绍、动画效果实现、附加三个漂亮的进度条
ProgressBar自定义的介绍.动画效果实现.附加三个漂亮的进度条 shape属性介绍: corners 圆角 gradient 渐变 padding 内容离边界距离 size 大小 ...
最新文章
- Android第十五课 Jni自带的iconv库不支持GBK转码
- ubuntu12.04手动安装virtualbox增强功能
- python可以做什么工作好-学Python能找到什么工作?这4种工作最热门!
- 【科普】数据中心“容灾”和“备份”的区别
- 【二分】数列分段(ybtoj 二分-1-1)
- Mssql 跨域查询
- 万兆网卡实际吞吐量_案例探索 | 千兆/万兆网卡每秒转发包数处理能力上限到底有多大?...
- Unknown column 'password_lifetime' in 'field list';创建数据库时创建用户,修改用户时报错
- linux ls -l 日期乱码,请教-关于ls-l的日期显示问题
- 【查找资料】冰点文档下载免费下载百度、豆丁、丁香、畅享、MBALib、道客巴巴、Book118等文库文档
- DPDK——IP分片和重组库
- layui 省市区 三级联动 单选
- Vue使用Echarts控件实现图表设计
- Android 系统(93)---android 怎么判断手机号是移动还是联通还是电信
- 第二章 Spring MVC入门 —— 跟开涛学SpringMVC
- html5柱状图模板,html5生成柱状图(条形图)
- 纽约大学计算机和信息科学专业,纽约大学与罗切斯特大学计算机科学专业比较...
- 检测网络不通时自动重启计算机,电脑连不上网怎么办?6个最简单最实用的解决办法,帮你轻松搞定!...
- 创维30周年庆典举行,中国制造业标杆向千亿目标加速冲刺
- 狄克斯特拉算法(Dijkstra)详细解释