自定义 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);
- }
- @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();
- }
- 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 属性的动画的更多相关文章
- 自定义Property属性动画
同步发表于 http://avenwu.net/customlayout/2015/04/06/custom_property_animation/ 代码获取 git clone https://gi ...
- Core Animation 文档翻译 (第七篇)——改变Layer的默认动画
前言 核心动画使用action对象实现它的可视化动画.一个action对象是指遵循CAAction协议并定义了Layer相关的动画行为的对象.所有的CAAnimation对象实现了这个协议,无论何时L ...
- ios开发图层layer与核心动画二:CATransform3D,CAlayear和UIView区别,layer的position和anchorpoint
一:CATransform3D #import "ViewController.h" @interface ViewController () @property (weak, n ...
- Android动画效果之自定义ViewGroup添加布局动画
前言: 前面几篇文章介绍了补间动画.逐帧动画.属性动画,大部分都是针对View来实现的动画,那么该如何为了一个ViewGroup添加动画呢?今天结合自定义ViewGroup来学习一下布局动画.本文将通 ...
- iOS开发UI篇—CAlayer(自定义layer)
iOS开发UI篇—CAlayer(自定义layer) 一.第一种方式 1.简单说明 以前想要在view中画东西,需要自定义view,创建一个类与之关联,让这个类继承自UIView,然后重写它的Draw ...
- 【UWP】对 Thickness 类型属性进行动画
好几个月没写 blog 了,一个是在忙新版的碧影壁纸,另一方面是等(观望)周年更新的 api(不过现在还是比较失望,仍然没法支持矩形以外的 Clip).闲话少说,进入主题. 在 UWP 中,出于性能考 ...
- iOS 自定义layer的两种方式
在iOS中,你能看得见摸得着的东西基本都是UIView,比如一个按钮,一个标签,一个文本输入框,这些都是UIView: 其实UIView之所以能显示在屏幕上,完全是因为它内部的一个图层 在创建UIVi ...
- iOS开发UI篇—自定义layer
一.第一种方式 1.简单说明 以前想要在view中画东西,需要自定义view,创建一个类与之关联,让这个类继承自UIView,然后重写它的DrawRect:方法,然后在该方法中画图. 绘制图形的步骤: ...
- iOS-自定义Model转场动画-仿酷我音乐播放器效果
周末,闲来无事,仿写了酷我音乐播放器效果: 效果图如下: 实现思路: 1.实现手势处理视图旋转 2.自定义Model动画: 1.手势是利用了一个UIPanGestureRecognizer手势: 注意 ...
随机推荐
- C++11 静态断言(static_assert)
简介 C++0x中引入了static_assert这个关键字,用来做编译期间的断言,因此叫做静态断言. 其语法很简单:static_assert(常量表达式,提示字符串). 如果第一个参数常量表达 ...
- Java Builder模式 体验(二)
在上篇文章中,对Java Builder模式的使用体验主要是从Builder对构造器改造方面的优秀特性来说的,感觉并没有从Java Builder模式本身的功能和作用去写,因此决定再从Build ...
- IOS6和IOS7的屏幕适配问题
自从IOS7出来以后,以前写在IOS6上或者更低版本的程序,跑在IOS7的模拟器上就会出现一些问题.最大的问题就是,所有的UI空间都会统一向上移动20个点(如果空间的y值为0,就会被StatusBar ...
- LB集群
LB集群 1. LB.LVS介绍LB集群是load balance 集群的简写,翻译成中文就是负载均衡集群LVS是一个实现负载均衡集群的开源软件项目 LVS架构从逻辑上可分为调度层(Directo ...
- 常用aliyun公共资源列表
公共DNS 223.5.5.5 223.6.6.6 源软件镜像站点 mirros.aliyun.com NTP服务器 unix like ntp1-7. ...
- SuperSocket学习笔记(二)
上一篇博客SuperSocket学习笔记(一)说明了怎么快速搭建一个服务器端,这篇文章我想深挖一下SuperSocket 1. 每一个客户端连接到服务器端时,服务器端会将客户端的信息保存到一个Sess ...
- 附加到IIS调试出现不会命中断点
当项目附加到IIS进行调试时,如果在IIS中没有配置该项目则在设置断点是会出现:当前不会命中断点 还没有为该文档加载任何符号
- git教程 入门
快速上传已有代码到github 如何将最新代码上传到github,这里讲本地已有项目文件的情况(假如本地有一个helloworld的工程目录,目录中有很多项目文件.),步骤如下: 前提:已安装git客 ...
- 把int类型值转换成int数组(不通过string类型转换)
只适合初学者 今天同事问了我不通过string类型把int类型值123589转换成int[]数组.我想了想于是写了出来,其实不难.看你小学数学学得好不好.言归正传. 先不说代码,举个列子就知道怎么玩了 ...
- meta 属性
几乎所有的网页里,我们可以看到类似下面这段的html代码:<head><meta http-equiv="content-Type" content=" ...