自定义 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手势: 注意 ...
随机推荐
- java开发之基础篇2
一.java开发环境的搭建 下载和安装jdk.版本自己看着办! 1 JAVA_HOME C:\Program Files\Java\jdk1.7.0_25 2 path C:\Program File ...
- jquery,js常用特效名称
- 用web技术开发出原生的App应用的体会(1)
本人是是个前端小白,学前端已经有半年的时间了,前几天开了个博客,希望记录自己学习历程的点滴. 今天要写的是关于用html,css,javascript等web技术开发原生的app应用. 总所周知,we ...
- css画下图
通常我看到这种效果,都是直接ps解决,但是不断重申性能的今天,显然不适应时代的需求啊! 今天看到群里有人问这种效果怎么做了,我在思考的时候,有人已经给出答案了: 我就测试了一下,发现确实可以实现,总结 ...
- js 操作剪切板
1.IE浏览器 window.clipboardData: setData() //设置值 getData()//获取值 clearData()//删除值 /******* ** IE 浏览器下支持w ...
- OOM总结
本文主要信息是来自互联网,我只是自己做了一点总结和摘要. OOM发生的原因 简单的说通过不同的内存分配方式对不同的对象进行操作,会因为android系统版本的差异而产生不同的行为.主要是2.0和4.0 ...
- C#中的表达式树的浅解
表达式树可以说是Linq的核心之一,为什么是Linq的核心之一呢?因为表达式树使得c#不再是仅仅能编译成IL,我们可以通过c#生成一个表达式树,将结果作为一个中间格式,在将其转换成目标平台上的本机语言 ...
- javascript判断键盘按键
window.document.onkeydown = disableRefresh; function disableRefresh(evt){ evt = (evt) ? evt : window ...
- Homebrew -- Mac软件管家(套件管理yun……)
也许是之前使用linux系统的时候总是习惯使用wget 在mac中只有curl,有点略显不习惯 于是乎某天在搜索mac开发者的时候发现了Homebrew这个东西 ok,是那么句话--惰性是人的天性 有 ...
- CM3存储器系统
1.位带(Bit-Band):如1M的地址都可以用bit访问,然后用32M的地址对应这1M的地址.其中这32M地址的每个字的最低位对应那1M可bit寻址的每个位.