自定义 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手势: 注意 ...
随机推荐
- 【Nginx】事件和连接
不同的操作系统相应不同的事件驱动机制.在Linux 2.6之后使用epoll机制.相应的事件驱动模块是ngx_epoll_module.Nginx的ngx_event_core_module模块依据操 ...
- [CSAPP笔记][第十一章网络编程]
第十一章 网络编程 我们需要理解基本的客户端-服务端编程模型,以及如何编写使用因特网提供的服务的客户端-服务端程序. 最后,我们将把所有这些概念结合起来,开发一个小的但功能齐全的Web服务器,能够为真 ...
- DEV GridControl 导出到Excel
SaveFileDialog saveFileDialog = new SaveFileDialog(); saveFileDialog.Title = "导出Excel"; sa ...
- HTML与CSS入门——第九章 使用颜色
知识点: 1.为网站选择颜色的方法 2.颜色在Web上的工作方式 3.使用十六进制颜色值的方法 4.使用CSS设置背景.文本和边框颜色的方法 9.1 选择颜色的最佳方法: 直白地说:根据用户群体找到最 ...
- 开始学javascript基础
JavaScript非常值得我们学习. 1)所有主浏览器都支持JavaScript. 2) 目前,全世界大部分网页都使用JavaScript. 3) 它可以使网页呈现各种动态效果. 4)作为一个Web ...
- linux删除ORACLE【weber出品必属精品】
关闭数据库 sqlplus / as sysdba shutdown abort 清除oracle软件 su - oracle cd $ORACLE_BASE rm -rf * rm -rf /etc ...
- 委托,匿名方法,Lambda,泛型委托,表达式树
一.委托:完成一个委托应分三个步骤://step01:首先用delegate定义一个委托;public delegate int CalculatorAdd(int x, int y);//step0 ...
- SQL Server类型与C#类型对应关系
SQL类型 C#类型 bit bool tinyint byte smallint short int int bigint long real float float double money de ...
- winPcap_1_开篇
什么是WinPcap WinPcap是一个基于Win32平台的,用于捕获网络数据包并进行分析的开源库. 因为有些应用程序需要直接访问网络中的数据包.也就是说,那些应用程序需要访问原始数据包,即没有被操 ...
- C# winform 窗体 彻底退出窗体的方法
1.this.Close(); 只是关闭当前窗口,若不是主窗体的话,是无法退出程序的,另外若有托管线程(非主线程),也无法干净地退出: 2.Application.Exit(); 强制所有消 ...