仓酷云

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 4007|回复: 18
打印 上一主题 下一主题

[学习教程] IOS教程之Layer 中自界说属性的动画仓酷云

[复制链接]
金色的骷髅 该用户已被删除
跳转到指定楼层
楼主
发表于 2015-1-18 11:30:22 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
管理所有设备发生的事件比如屏幕旋转屏幕关闭或者一些其他的程序的控制逻辑也应该写在这里他的初始化函数是-(id)initWithNibName:(NSString*)nibNamebundle:(NSBundle*)nibBundle后面那个NibName是InterfaceBuilder里设计的界面现在IB已经集成到XCode里了默许情形下,CALayer及其子类的尽年夜部分尺度属性都能够实行动画,不管是增加一个CAAnimation到Layer(显式动画),亦或是为属性指定一个举措然后修正它(隐式动画)。
但偶然候我们但愿能同时为好几个属性增加动画,使它们看起来像是一个动画一样;大概,我们必要实行的动画不克不及经由过程利用尺度Layer属性动画来完成。
在本文中,我们将会商怎样子类化CALayer并增加我们本人的属性,以便对照简单地创立那些假如以其他体例完成起来会很贫苦的动画效果。
一样平常说来,我们但愿增加到CALayer的子类上的可动画属性有三品种型:


  • 能直接动画Layer(或其子类)的一个或多个尺度属性的属性。
  • 能触发Layer面前的图象(即contents属性)重绘的属性。
  • 不触及Layer重绘或对任何已有属性实行动画的属性。
直接属性动画

能直接修正别的尺度Layer属性的自界说属性是这些选项中最复杂的。它们仅仅只是自界说setter办法。然后将它们的输出转换为合用于创立动画的一个或多个分歧的值。
假如被我们设置的属性已预设好尺度动画,那我们完整不必要编写任何实践的动画代码,由于我们修正这些属性后,它们就会承继任何被设置在以后CATransaction上的动画设置,而且主动实行动画。
换句话说,即便CALayer不晓得怎样对我们自界说的属性举行动画,它仍然能对因自界说属性被改动而引发的别的可见反作用举行动画,而这刚好就是我们所必要的。
为了演示这类办法,让我们来创立一个复杂的摹拟时钟,以后我们可使用被声明为NSDate范例time属性来设置它的工夫。我会将从创立一个静态的时钟面盘入手下手。这个时钟包括三个CAShapeLayer实例——一个用于时钟面盘的圆形Layer和两个用于时针和分针的长方形Sublayer。
  1. @interfaceClockFace:CAShapeLayer@property(nonatomic,strong)NSDate*time;@end@interfaceClockFace()//公有属性@property(nonatomic,strong)CAShapeLayer*hourHand;@property(nonatomic,strong)CAShapeLayer*minuteHand;@end@implementationClockFace-(id)init{if((self=[superinit])){self.bounds=CGRectMake(0,0,200,200);self.path=[UIBezierPathbezierPathWithOvalInRect:self.bounds].CGPath;self.fillColor=[UIColorwhiteColor].CGColor;self.strokeColor=[UIColorblackColor].CGColor;self.lineWidth=4;self.hourHand=[CAShapeLayerlayer];self.hourHand.path=[UIBezierPathbezierPathWithRect:CGRectMake(-2,-70,4,70)].CGPath;self.hourHand.fillColor=[UIColorblackColor].CGColor;self.hourHand.position=CGPointMake(self.bounds.size.width/2,self.bounds.size.height/2);[selfaddSublayer:self.hourHand];self.minuteHand=[CAShapeLayerlayer];self.minuteHand.path=[UIBezierPathbezierPathWithRect:CGRectMake(-1,-90,2,90)].CGPath;self.minuteHand.fillColor=[UIColorblackColor].CGColor;self.minuteHand.position=CGPointMake(self.bounds.size.width/2,self.bounds.size.height/2);[selfaddSublayer:self.minuteHand];}returnself;}@end
复制代码
同时我们要设置一个包括UIDatePicker的基础的ViewController,如许我们就可以测试我们的Layer(日期选择器在Storyboard里设置)了:
  1. @interfaceViewController()@property(nonatomic,strong)IBOutletUIDatePicker*datePicker;@property(nonatomic,strong)ClockFace*clockFace;@end@implementationViewController-(void)viewDidLoad{[superviewDidLoad];//增加时钟面板Layerself.clockFace=[[ClockFacealloc]init];self.clockFace.position=CGPointMake(self.view.bounds.size.width/2,150);[self.view.layeraddSublayer:self.clockFace];//设置默许工夫self.clockFace.time=[NSDatedate];}-(IBAction)setTime{self.clockFace.time=self.datePicker.date;}@end
复制代码
如今我们只必要完成time属性的setter办法。这个办法利用NSCalendar将工夫变成小时和分钟,以后我们将它们转换为角坐标。然后我们就能够利用这些角度往天生两个CGAffineTransform以扭转时针和分针。
  1. -(void)setTime:(NSDate*)time{_time=time;NSCalendar*calendar=[[NSCalendaralloc]initWithCalendarIdentifier:NSGregorianCalendar];NSDateComponents*components=[calendarcomponents:NSHourCalendarUnit|NSMinuteCalendarUnitfromDate: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);}
复制代码
了局看起来像如许:

<br>
你能够从GitHub高低载这个项目看看。
如你所见,我们其实没有做甚么太费头脑的事变;我们并没有创立一个新的可动画属性,而只是在单个办法里设置了几个尺度可动画Layer属性罢了。但是,假如我们想创立的动画其实不能映照就任何已有的Layer属性上时,该怎样办呢?
动画Layer内容

假定不利用几个分别的Layer来完成我们的时钟面板,那我们能够改用CoreGraphics来绘制时钟。(这一般会下降功能,但我们能够设想我们所要完成的效果必要很多庞大的画图操纵,而它们很难用惯例的Layer属性和transform来复制。)我们要怎样做呢?
与NSManagedObject很相似,CALayer具无为任何被声明的属性天生dynamic的setter和getter的才能。在我们以后的完成中,我们让编译器往synthesize了time属性的ivar和getter办法,而我们本人完成了setter办法。但让我们来改动一下:抛弃我们的setter并将属性标志为@dynamic。同时我们也抛弃分别的时针和分针Layer,由于我们将本人往绘制它们。
  1. @interfaceClockFace()@end@implementationClockFace@dynamictime;-(id)init{if((self=[superinit])){self.bounds=CGRectMake(0,0,200,200);}returnself;}@end
复制代码
在我们入手下手之前,必要先做一个小调剂:由于不幸的是,CALayer不晓得怎样对NSDate属性举行插值(interpolate)(比方,固然它能够处置数字范例和别的比方CGColor和CGAffineTransform如许的范例,但它不克不及主动天生分歧的NSDate实例之间的两头值)。我们能够保存我们的自界说setter办法并用它设置另外一个等价于NSTimeInterval的静态属性(这是一个数字值,能够被插值),但为了坚持例子的复杂性,我们会用一个浮点值交换NSDate属性来表征时钟的小时。我们还更新了用户界面,如今利用一个复杂的UITextField来设置浮点值,而不再利用日期选择器:
  1. @interfaceViewController()<UITextFieldDelegate>@property(nonatomic,strong)IBOutletUITextField*textField;@property(nonatomic,strong)ClockFace*clockFace;@end@implementationViewController-(void)viewDidLoad{[superviewDidLoad];//增加时钟面板Layerself.clockFace=[[ClockFacealloc]init];self.clockFace.position=CGPointMake(self.view.bounds.size.width/2,150);[self.view.layeraddSublayer:self.clockFace];}-(BOOL)textFieldShouldReturn:(UITextField*)textField{[textFieldresignFirstResponder];returnYES;}-(void)textFieldDidEndEditing:(UITextField*)textField{self.clockFace.time=[textField.textfloatValue];}@end
复制代码
如今,既然我们已移除自界说的setter办法,那我们要怎样才干晓得time属性的改动呢?我们必要一个不管什么时候time属性改动时都能主动关照CALayer的体例,如许它才好重绘它的内容。我们经由过程覆写+needsDisplayForKey:办法便可做到这一点,以下:
  1. +(BOOL)needsDisplayForKey:(NSString*)key{if([@"time"isEqualToString:key]){returnYES;}return[superneedsDisplayForKey:key];}
复制代码
这就告知了Layer,不管什么时候time属性被修正,它都必要挪用-display办法。如今我们就覆写-display办法,增加一个NSLog语句打印出time的值:
  1. -(void)display{NSLog(@"time:%f",self.time);}
复制代码
假如我们设置time属性为1.5,我们就会看到-display被挪用,打印出新值:
  1. 2014-04-2822:37:04.253ClockFace[49145:60b]time:1.500000
复制代码
但这还不是我们真正想要的;我们但愿time属功能在旧值和新值之间在几帧以内做一个光滑的过渡动画。为了完成这一点,我们必要为time属性指定一个动画(或“举措(action)”),而经由过程覆写-actionForKey:办法就可以做到:
  1. -(id<CAAction>)actionForKey:(NSString*)key{if([keyisEqualToString:@"time"]){CABasicAnimation*animation=[CABasicAnimationanimationWithKeyPath:key];animation.timingFunction=[CAMediaTimingFunctionfunctionWithName:kCAMediaTimingFunctionLinear];animation.fromValue=@(self.time);returnanimation;}return[superactionForKey:key];}
复制代码
如今,假如我们再次设置time属性,我们就会看到-display被屡次挪用。挪用的次数约莫为每秒60次,至于动画的长度,默许为0.25秒,约莫是15帧:
  1. 2014-04-2822:37:04.253ClockFace[49145:60b]time:1.5000002014-04-2822:37:04.255ClockFace[49145:60b]time:1.5000002014-04-2822:37:04.351ClockFace[49145:60b]time:1.5000002014-04-2822:37:04.370ClockFace[49145:60b]time:1.5000002014-04-2822:37:04.388ClockFace[49145:60b]time:1.5000002014-04-2822:37:04.407ClockFace[49145:60b]time:1.5000002014-04-2822:37:04.425ClockFace[49145:60b]time:1.5000002014-04-2822:37:04.443ClockFace[49145:60b]time:1.5000002014-04-2822:37:04.461ClockFace[49145:60b]time:1.5000002014-04-2822:37:04.479ClockFace[49145:60b]time:1.5000002014-04-2822:37:04.497ClockFace[49145:60b]time:1.5000002014-04-2822:37:04.515ClockFace[49145:60b]time:1.5000002014-04-2822:37:04.755ClockFace[49145:60b]time:1.500000
复制代码
因为某些缘故原由,当我们在每一个两头点打印time值时,我们一向看到的是终极值。为什么不克不及失掉插值呢?由于我们检察的是毛病的time属性。
当你设置某个CALayer的某个属性,你实践设置的是modelLayer的值——这里的modelLayer暗示正在举行的动画停止时,Layer所到达的终极形态。假如你取modelLayer的值,它就老是给你它被设置到的终极值。
但毗连到modelLayer的是所谓的presentationLayer——它是modelLayer的一个拷贝,但它的值所暗示的是以后的,两头动画形态。假如我们修正-display办法往打印Layer的presentationLayer的time属性,那我们就会看到我们所希冀的插值。(同时我们也利用presentationLayer的time属性来猎取动画的入手下手值,替换self.time):
  1. @interfaceViewController()@property(nonatomic,strong)IBOutletUIDatePicker*datePicker;@property(nonatomic,strong)ClockFace*clockFace;@end@implementationViewController-(void)viewDidLoad{[superviewDidLoad];//增加时钟面板Layerself.clockFace=[[ClockFacealloc]init];self.clockFace.position=CGPointMake(self.view.bounds.size.width/2,150);[self.view.layeraddSublayer:self.clockFace];//设置默许工夫self.clockFace.time=[NSDatedate];}-(IBAction)setTime{self.clockFace.time=self.datePicker.date;}@end0
复制代码
上面是打印出的值:
  1. @interfaceViewController()@property(nonatomic,strong)IBOutletUIDatePicker*datePicker;@property(nonatomic,strong)ClockFace*clockFace;@end@implementationViewController-(void)viewDidLoad{[superviewDidLoad];//增加时钟面板Layerself.clockFace=[[ClockFacealloc]init];self.clockFace.position=CGPointMake(self.view.bounds.size.width/2,150);[self.view.layeraddSublayer:self.clockFace];//设置默许工夫self.clockFace.time=[NSDatedate];}-(IBAction)setTime{self.clockFace.time=self.datePicker.date;}@end1
复制代码
以是如今我们所要做就是画出时钟。我们将利用一般的CoreGraphics函数以绘制到一个GraphicsContext下去做到这一点,然后将发生出图象设置为我们Layer的contents。上面是更新后的-display办法:
  1. @interfaceViewController()@property(nonatomic,strong)IBOutletUIDatePicker*datePicker;@property(nonatomic,strong)ClockFace*clockFace;@end@implementationViewController-(void)viewDidLoad{[superviewDidLoad];//增加时钟面板Layerself.clockFace=[[ClockFacealloc]init];self.clockFace.position=CGPointMake(self.view.bounds.size.width/2,150);[self.view.layeraddSublayer:self.clockFace];//设置默许工夫self.clockFace.time=[NSDatedate];}-(IBAction)setTime{self.clockFace.time=self.datePicker.date;}@end2
复制代码
了局看起来以下:

<br>
如你所见,分歧于第一个时钟动画,跟着时针的变更,分针实践上对每个小时城市转上满满一圈(就像一个真实的时钟那样),而不单单只是经由过程最短的路径挪动到它的终极地位;由于我们正在动画的是time值自己而不单单是时针或分针的地位,以是高低文信息被保存了。
经由过程如许的体例绘制一个时钟并非很幻想,由于CoreGraphics函数没有硬件减速,大概会引发动画帧数的下落。另外一种能每秒重绘contents图象60次的体例是用一个数组存储一些事后绘制好的图象,然后基于符合的插值复杂的选择对应的图象便可。完成代码也许以下:
  1. @interfaceViewController()@property(nonatomic,strong)IBOutletUIDatePicker*datePicker;@property(nonatomic,strong)ClockFace*clockFace;@end@implementationViewController-(void)viewDidLoad{[superviewDidLoad];//增加时钟面板Layerself.clockFace=[[ClockFacealloc]init];self.clockFace.position=CGPointMake(self.view.bounds.size.width/2,150);[self.view.layeraddSublayer:self.clockFace];//设置默许工夫self.clockFace.time=[NSDatedate];}-(IBAction)setTime{self.clockFace.time=self.datePicker.date;}@end3
复制代码
经由过程制止在每帧里都用高贵的软件绘制,我们能改良动画的功能,但价值是我们必要在内存里存储一切事后绘制的动画帧图象,关于一个庞大的动画来讲,这大概形成惊人的内存华侈。
但这提出了一个风趣的大概性。假如我们完整不在-display里更新contents图象会产生甚么?我们做一些别的的事变如何?
非可视属性的动画

在-display里更新别的Layer属性就是不用要的,由于我们能够很复杂地间接对任何如许的属性做动画,好像我们在第一个时钟面板例子里所做的那样。但假如我们设置一些别的的工具,好比某些完整和Layer不相干的工具,会如何呢?
上面的代码利用一个CALayer分离AVAudioPlayer来创立一个可动画的音量把持器。经由过程把音量绑定到dynamic的Layer属性上,我们可使用CoreAnimation的属性插值来光滑的在两个分歧的音量之间突变,以一样的体例我们能够动画Layer上的任何自界说属性:
  1. @interfaceViewController()@property(nonatomic,strong)IBOutletUIDatePicker*datePicker;@property(nonatomic,strong)ClockFace*clockFace;@end@implementationViewController-(void)viewDidLoad{[superviewDidLoad];//增加时钟面板Layerself.clockFace=[[ClockFacealloc]init];self.clockFace.position=CGPointMake(self.view.bounds.size.width/2,150);[self.view.layeraddSublayer:self.clockFace];//设置默许工夫self.clockFace.time=[NSDatedate];}-(IBAction)setTime{self.clockFace.time=self.datePicker.date;}@end4
复制代码
我们能够经由过程利用一个复杂的有着播放、中断、音量增年夜和音量减小按钮的ViewController来做测试:

Model的改变最好通过Notification来传播之前吃过这样的亏最好不要用delegate模式)UIViewController
再见西城 该用户已被删除
沙发
发表于 2015-1-24 15:48:35 | 只看该作者
自从苹果公司开放iOS SDK以来,大量的国内外的软件开发者将关注的目光聚集在苹果的iOS平台上。由于iPhone和iPad自一出现就给人带来了颠覆性的感觉
若相依 该用户已被删除
板凳
发表于 2015-2-2 06:22:46 | 只看该作者
iPhone文件系统:创建、重命名以及删除文件,NSFileManager中包含了用来查询单词库目录、创建、重命名、删除目录以及获取/设置文件属性的方法(可读性,可编写性等等)。
再现理想 该用户已被删除
地板
发表于 2015-2-7 08:27:03 | 只看该作者
近期由于IOS7的发布,所以应用的适配潮可谓是都搞的锣鼓喧天,甚是热闹,因此呢,因适配IOS7而产生的问题也是铺天盖地的卷来,
愤怒的大鸟 该用户已被删除
5#
发表于 2015-2-11 21:21:00 | 只看该作者
众多研发人员积极参与到iOS平台的开发中来也就不足为奇了。
兰色精灵 该用户已被删除
6#
发表于 2015-2-15 12:00:32 | 只看该作者
在百度搜索你想要了解的类名(苹果的cocoa和cocoatouch框架的类名很有特点很容易搜到,前缀都是NS or UI),看别人写的博客详解
海妖 该用户已被删除
7#
发表于 2015-2-25 00:50:08 | 只看该作者
培训的时候很痛苦,每天要待12个小时,上午讲课,下午和晚自习解决作业,看文档,学习的时候感觉就是资料太少,而且看着资料也不明所以,非常痛苦,
小妖女 该用户已被删除
8#
发表于 2015-3-7 14:50:40 | 只看该作者
中国如今已然发展成为一个软件大国,软件人才的数量跃居全球之首。当然,在苹果平台的开发领域,也保持了相当强劲的发展势头。然而,很多初入iOS开发门槛的开发者,
活着的死人 该用户已被删除
9#
发表于 2015-3-15 07:34:11 | 只看该作者
看完这个你就可以有多种选择来踏入做应用的阶段
10#
发表于 2015-3-17 00:06:58 | 只看该作者
其实在培训的过程中,学习到最多的就是查资料的方式,当时感觉老师好坑,什么都不告诉我们,让我们自己去查,但是现在觉得还是要自己解决问题,这样才能理解的更加深入。
只想知道 该用户已被删除
11#
发表于 2015-3-27 11:11:03 | 只看该作者
到大三时,学院与我去培训的机构成立了实习基地,并让我们寒假去实习了一段时间,感觉还不错,于是在大四的时候去培训了
冷月葬花魂 该用户已被删除
12#
发表于 2015-3-31 09:44:33 | 只看该作者
因为我们老师也是自学的,给我们讲课说的最多的就是百度,谷歌,查文档。
爱飞 该用户已被删除
13#
发表于 2015-6-14 01:18:27 | 只看该作者
从C语言入门,因为IOS开发用的是OC语言,是在C基础上的,不过也跟C不是很搭界,你可以直接学习OC语言也可以,
第二个灵魂 该用户已被删除
14#
发表于 2015-7-11 13:12:46 | 只看该作者
iPhone文件系统NSFileManager讲解是本文要介绍的内容,主要是通过iphone文件系统来学习NSFileManager的使用方法,具体内容来看本文详解。
谁可相欹 该用户已被删除
15#
发表于 2015-7-13 03:56:53 | 只看该作者
才在自己的Windows电脑上安装配置成功了一个完美的Mac OS X Lion(10.7.4)系统,而且下载了Xcode4.5的最新版本。虽然不能实机调试,但是作为iOS开发学习已经非常完美了。
小魔女 该用户已被删除
16#
发表于 2015-7-16 16:07:22 | 只看该作者
要学会通过各种方法将面前的事情变成自己感兴趣的,那专研起来就不会是无聊和折磨了。
若天明 该用户已被删除
17#
发表于 2015-7-17 23:02:20 | 只看该作者
看完这个你就可以有多种选择来踏入做应用的阶段
深爱那片海 该用户已被删除
18#
发表于 2015-7-23 04:05:01 | 只看该作者
到大三时,学院与我去培训的机构成立了实习基地,并让我们寒假去实习了一段时间,感觉还不错,于是在大四的时候去培训了
灵魂腐蚀 该用户已被删除
19#
发表于 2015-7-23 22:33:56 | 只看该作者
我也从简单的状态栏适配开始,先研究了下关于状态栏的适配,特总结如下,供广大网友一起讨论交流。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|仓酷云 鄂ICP备14007578号-2

GMT+8, 2024-12-22 23:02

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表