本文轉(zhuǎn)載自:http://www.cocoachina.com/ios/20150105/10829.html? 為了防止cocochina以后刪除該文章攒菠,故轉(zhuǎn)載至此拳锚;
圖層時(shí)間
時(shí)間和空間最大的區(qū)別在于背伴,時(shí)間不能被復(fù)用 -- 弗斯特梅里克
在上面兩章中,我們探討了可以用CAAnimation和它的子類實(shí)現(xiàn)的多種圖層動(dòng)畫(huà)谈秫。動(dòng)畫(huà)的發(fā)生是需要持續(xù)一段時(shí)間的雇庙,所以計(jì)時(shí)對(duì)整個(gè)概念來(lái)說(shuō)至關(guān)重要。在這一章中钉跷,我們來(lái)看看CAMediaTiming,看看Core Animation是如何跟蹤時(shí)間的爷辙。
CAMediaTiming協(xié)議
CAMediaTiming協(xié)議定義了在一段動(dòng)畫(huà)內(nèi)用來(lái)控制逝去時(shí)間的屬性的集合,CALayer和CAAnimation都實(shí)現(xiàn)了這個(gè)協(xié)議膝晾,所以時(shí)間可以被任意基于一個(gè)圖層或者一段動(dòng)畫(huà)的類控制。
持續(xù)和重復(fù)
我們?cè)诘诎苏隆帮@式動(dòng)畫(huà)”中簡(jiǎn)單提到過(guò)duration(CAMediaTiming的屬性之一)血当,duration是一個(gè)CFTimeInterval的類型(類似于NSTimeInterval的一種雙精度浮點(diǎn)類型)幻赚,對(duì)將要進(jìn)行的動(dòng)畫(huà)的一次迭代指定了時(shí)間。
這里的一次迭代是什么意思呢箩退?CAMediaTiming另外還有一個(gè)屬性叫做repeatCount,代表動(dòng)畫(huà)重復(fù)的迭代次數(shù)佳谦。如果duration是2戴涝,repeatCount設(shè)為3.5(三個(gè)半迭代),那么完整的動(dòng)畫(huà)時(shí)長(zhǎng)將是7秒。
duration和repeatCount默認(rèn)都是0矢棚。但這不意味著動(dòng)畫(huà)時(shí)長(zhǎng)為0秒府喳,或者0次,這里的0僅僅代表了“默認(rèn)”钝满,也就是0.25秒和1次,你可以用一個(gè)簡(jiǎn)單的測(cè)試來(lái)嘗試為這兩個(gè)屬性賦多個(gè)值弯蚜,如清單9.1碎捺,圖9.1展示了程序的結(jié)果。
清單9.1 測(cè)試duration和repeatCount
@interface?ViewController?()
@property?(nonatomic,?weak)?IBOutlet?UIView?*containerView;
@property?(nonatomic,?weak)?IBOutlet?UITextField?*durationField;
@property?(nonatomic,?weak)?IBOutlet?UITextField?*repeatField;
@property?(nonatomic,?weak)?IBOutlet?UIButton?*startButton;
@property?(nonatomic,?strong)?CALayer?*shipLayer;
@end
@implementation?ViewController
-?(void)viewDidLoad
{
[super?viewDidLoad];
//add?the?ship
self.shipLayer?=?[CALayer?layer];
self.shipLayer.frame?=?CGRectMake(0,?0,?128,?128);
self.shipLayer.position?=?CGPointMake(150,?150);
self.shipLayer.contents?=?(__bridge?id)[UIImage?imageNamed:?@"Ship.png"].CGImage;
[self.containerView.layer?addSublayer:self.shipLayer];
}
-?(void)setControlsEnabled:(BOOL)enabled
{
for?(UIControl?*control?in?@[self.durationField,?self.repeatField,?self.startButton])?{
control.enabled?=?enabled;
control.alpha?=?enabled??1.0f:?0.25f;
}
}
-?(IBAction)hideKeyboard
{
?[self.durationField?resignFirstResponder];
[self.repeatField?resignFirstResponder];
}
-?(IBAction)start
{
CFTimeInterval?duration?=?[self.durationField.text?doubleValue];
float?repeatCount?=?[self.repeatField.text?floatValue];
//animate?the?ship?rotation
CABasicAnimation?*animation?=?[CABasicAnimation?animation];
animation.keyPath?=?@"transform.rotation";
animation.duration?=?duration;
animation.repeatCount?=?repeatCount;
animation.byValue?=?@(M_PI?*?2);
animation.delegate?=?self;
[self.shipLayer?addAnimation:animation?forKey:@"rotateAnimation"];
//disable?controls
[self?setControlsEnabled:NO];
}
-?(void)animationDidStop:(CAAnimation?*)anim?finished:(BOOL)flag
{
//reenable?controls
[self?setControlsEnabled:YES];
}
@end
圖9.1 演示duration和repeatCount的測(cè)試程序
創(chuàng)建重復(fù)動(dòng)畫(huà)的另一種方式是使用repeatDuration屬性晋柱,它讓動(dòng)畫(huà)重復(fù)一個(gè)指定的時(shí)間诵叁,而不是指定次數(shù)。你甚至設(shè)置一個(gè)叫做autoreverses的屬性(BOOL類型)在每次間隔交替循環(huán)過(guò)程中自動(dòng)回放碑诉。這對(duì)于播放一段連續(xù)非循環(huán)的動(dòng)畫(huà)很有用侥锦,例如打開(kāi)一扇門(mén),然后關(guān)上它(圖9.2)泪幌。
圖9.2 擺動(dòng)門(mén)的動(dòng)畫(huà)
對(duì)門(mén)進(jìn)行擺動(dòng)的代碼見(jiàn)清單9.2。我們用了autoreverses來(lái)使門(mén)在打開(kāi)后自動(dòng)關(guān)閉祸泪,在這里我們把repeatDuration設(shè)置為INFINITY,于是動(dòng)畫(huà)無(wú)限循環(huán)播放懂扼,設(shè)置repeatCount為INFINITY也有同樣的效果右蒲。注意repeatCount和repeatDuration可能會(huì)相互沖突,所以你只要對(duì)其中一個(gè)指定非零值瑰妄。對(duì)兩個(gè)屬性都設(shè)置非0值的行為沒(méi)有被定義。
清單9.2 使用autoreverses屬性實(shí)現(xiàn)門(mén)的搖擺
@interface?ViewController?()
@property?(nonatomic,?weak)?UIView?*containerView;
@end
@implementation?ViewController
-?(void)viewDidLoad
{
[super?viewDidLoad];
//add?the?door
CALayer?*doorLayer?=?[CALayer?layer];
doorLayer.frame?=?CGRectMake(0,?0,?128,?256);
doorLayer.position?=?CGPointMake(150?-?64,?150);
doorLayer.anchorPoint?=?CGPointMake(0,?0.5);
doorLayer.contents?=?(__bridge?id)[UIImage?imageNamed:?@"Door.png"].CGImage;
[self.containerView.layer?addSublayer:doorLayer];
//apply?perspective?transform
CATransform3D?perspective?=?CATransform3DIdentity;
perspective.m34?=?-1.0?/?500.0;
self.containerView.layer.sublayerTransform?=?perspective;
//apply?swinging?animation
CABasicAnimation?*animation?=?[CABasicAnimation?animation];
animation.keyPath?=?@"transform.rotation.y";
animation.toValue?=?@(-M_PI_2);
animation.duration?=?2.0;
animation.repeatDuration?=?INFINITY;
animation.autoreverses?=?YES;
[doorLayer?addAnimation:animation?forKey:nil];
}
@end
相對(duì)時(shí)間
每次討論到Core Animation灾挨,時(shí)間都是相對(duì)的竹宋,每個(gè)動(dòng)畫(huà)都有它自己描述的時(shí)間,可以獨(dú)立地加速秒拔,延時(shí)或者偏移飒硅。
beginTime指定了動(dòng)畫(huà)開(kāi)始之前的的延遲時(shí)間。這里的延遲從動(dòng)畫(huà)添加到可見(jiàn)圖層的那一刻開(kāi)始測(cè)量狡相,默認(rèn)是0(就是說(shuō)動(dòng)畫(huà)會(huì)立刻執(zhí)行)尽棕。
speed是一個(gè)時(shí)間的倍數(shù)喳挑,默認(rèn)1.0滔悉,減少它會(huì)減慢圖層/動(dòng)畫(huà)的時(shí)間,增加它會(huì)加快速度回官。如果2.0的速度歉提,那么對(duì)于一個(gè)duration為1的動(dòng)畫(huà)区转,實(shí)際上在0.5秒的時(shí)候就已經(jīng)完成了版扩。
timeOffset和beginTime類似,但是和增加beginTime導(dǎo)致的延遲動(dòng)畫(huà)不同礁芦,增加timeOffset只是讓動(dòng)畫(huà)快進(jìn)到某一點(diǎn),例如肖方,對(duì)于一個(gè)持續(xù)1秒的動(dòng)畫(huà)來(lái)說(shuō)未状,設(shè)置timeOffset為0.5意味著動(dòng)畫(huà)將從一半的地方開(kāi)始。
和beginTime不同的是娩践,timeOffset并不受speed的影響烹骨。所以如果你把speed設(shè)為2.0,把timeOffset設(shè)置為0.5吨岭,那么你的動(dòng)畫(huà)將從動(dòng)畫(huà)最后結(jié)束的地方開(kāi)始峦树,因?yàn)?秒的動(dòng)畫(huà)實(shí)際上被縮短到了0.5秒。然而即使使用了timeOffset讓動(dòng)畫(huà)從結(jié)束的地方開(kāi)始魁巩,它仍然播放了一個(gè)完整的時(shí)長(zhǎng),這個(gè)動(dòng)畫(huà)僅僅是循環(huán)了一圈葬馋,然后從頭開(kāi)始播放肾扰。
可以用清單9.3的測(cè)試程序驗(yàn)證一下,設(shè)置speed和timeOffset滑塊到隨意的值窗悯,然后點(diǎn)擊播放來(lái)觀察效果(見(jiàn)圖9.3)
清單9.3 測(cè)試timeOffset和speed屬性
@interface?ViewController?()
@property?(nonatomic,?weak)?IBOutlet?UIView?*containerView;
@property?(nonatomic,?weak)?IBOutlet?UILabel?*speedLabel;
@property?(nonatomic,?weak)?IBOutlet?UILabel?*timeOffsetLabel;
@property?(nonatomic,?weak)?IBOutlet?UISlider?*speedSlider;
@property?(nonatomic,?weak)?IBOutlet?UISlider?*timeOffsetSlider;
@property?(nonatomic,?strong)?UIBezierPath?*bezierPath;
@property?(nonatomic,?strong)?CALayer?*shipLayer;
@end
@implementation?ViewController
-?(void)viewDidLoad
{
[super?viewDidLoad];
//create?a?path
self.bezierPath?=?[[UIBezierPath?alloc]?init];
[self.bezierPath?moveToPoint:CGPointMake(0,?150)];
[self.bezierPath?addCurveToPoint:CGPointMake(300,?150)?controlPoint1:CGPointMake(75,?0)?controlPoint2:CGPointMake(225,?300)];
//draw?the?path?using?a?CAShapeLayer
CAShapeLayer?*pathLayer?=?[CAShapeLayer?layer];
pathLayer.path?=?self.bezierPath.CGPath;
pathLayer.fillColor?=?[UIColor?clearColor].CGColor;
pathLayer.strokeColor?=?[UIColor?redColor].CGColor;
pathLayer.lineWidth?=?3.0f;
[self.containerView.layer?addSublayer:pathLayer];
//add?the?ship
self.shipLayer?=?[CALayer?layer];
self.shipLayer.frame?=?CGRectMake(0,?0,?64,?64);
self.shipLayer.position?=?CGPointMake(0,?150);
self.shipLayer.contents?=?(__bridge?id)[UIImage?imageNamed:?@"Ship.png"].CGImage;
[self.containerView.layer?addSublayer:self.shipLayer];
//set?initial?values
[self?updateSliders];
}
-?(IBAction)updateSliders
{
CFTimeInterval?timeOffset?=?self.timeOffsetSlider.value;
self.timeOffsetLabel.text?=?[NSString?stringWithFormat:@"%0.2f",?imeOffset];
float?speed?=?self.speedSlider.value;
self.speedLabel.text?=?[NSString?stringWithFormat:@"%0.2f",?speed];
}
-?(IBAction)play
{
//create?the?keyframe?animation
CAKeyframeAnimation?*animation?=?[CAKeyframeAnimation?animation];
animation.keyPath?=?@"position";
animation.timeOffset?=?self.timeOffsetSlider.value;
animation.speed?=?self.speedSlider.value;
animation.duration?=?1.0;
animation.path?=?self.bezierPath.CGPath;
animation.rotationMode?=?kCAAnimationRotateAuto;
animation.removedOnCompletion?=?NO;
[self.shipLayer?addAnimation:animation?forKey:@"slide"];
}
@end
圖9.3 測(cè)試時(shí)間偏移和速度的簡(jiǎn)單的應(yīng)用程序
fillMode
對(duì)于beginTime非0的一段動(dòng)畫(huà)來(lái)說(shuō)蒋院,會(huì)出現(xiàn)一個(gè)當(dāng)動(dòng)畫(huà)添加到圖層上但什么也沒(méi)發(fā)生的狀態(tài)。類似的悦污,removeOnCompletion被設(shè)置為NO的動(dòng)畫(huà)將會(huì)在動(dòng)畫(huà)結(jié)束的時(shí)候仍然保持之前的狀態(tài)切端。這就產(chǎn)生了一個(gè)問(wèn)題彻坛,當(dāng)動(dòng)畫(huà)開(kāi)始之前和動(dòng)畫(huà)結(jié)束之后踏枣,被設(shè)置動(dòng)畫(huà)的屬性將會(huì)是什么值呢?
一種可能是屬性和動(dòng)畫(huà)沒(méi)被添加之前保持一致间驮,也就是在模型圖層定義的值(見(jiàn)第七章“隱式動(dòng)畫(huà)”马昨,模型圖層和呈現(xiàn)圖層的解釋)。
另一種可能是保持動(dòng)畫(huà)開(kāi)始之前那一幀屹篓,或者動(dòng)畫(huà)結(jié)束之后的那一幀匙奴。這就是所謂的填充,因?yàn)閯?dòng)畫(huà)開(kāi)始和結(jié)束的值用來(lái)填充開(kāi)始之前和結(jié)束之后的時(shí)間谍肤。
這種行為就交給開(kāi)發(fā)者了哗伯,它可以被CAMediaTiming的fillMode來(lái)控制。fillMode是一個(gè)NSString類型系任,可以接受如下四種常量:
kCAFillModeForwards
kCAFillModeBackwards
kCAFillModeBoth
kCAFillModeRemoved
默認(rèn)是kCAFillModeRemoved伴澄,當(dāng)動(dòng)畫(huà)不再播放的時(shí)候就顯示圖層模型指定的值剩下的三種類型向前,向后或者即向前又向后去填充動(dòng)畫(huà)狀態(tài)举农,使得動(dòng)畫(huà)在開(kāi)始前或者結(jié)束后仍然保持開(kāi)始和結(jié)束那一刻的值敞嗡。
這就對(duì)避免在動(dòng)畫(huà)結(jié)束的時(shí)候急速返回提供另一種方案(見(jiàn)第八章)航背。但是記住了棱貌,當(dāng)用它來(lái)解決這個(gè)問(wèn)題的時(shí)候,需要把removeOnCompletion設(shè)置為NO今魔,另外需要給動(dòng)畫(huà)添加一個(gè)非空的鍵障贸,于是可以在不需要?jiǎng)赢?huà)的時(shí)候把它從圖層上移除涩维。
層級(jí)關(guān)系時(shí)間
在第三章“圖層幾何學(xué)”中袁波,你已經(jīng)了解到每個(gè)圖層是如何相對(duì)在圖層樹(shù)中的父圖層定義它的坐標(biāo)系的。動(dòng)畫(huà)時(shí)間和它類似睡蟋,每個(gè)動(dòng)畫(huà)和圖層在時(shí)間上都有它自己的層級(jí)概念娃磺,相對(duì)于它的父親來(lái)測(cè)量叫倍。對(duì)圖層調(diào)整時(shí)間將會(huì)影響到它本身和子圖層的動(dòng)畫(huà),但不會(huì)影響到父圖層听诸。另一個(gè)相似點(diǎn)是所有的動(dòng)畫(huà)都被按照層級(jí)組合(使用CAAnimationGroup實(shí)例)蚕泽。
對(duì)CALayer或者CAGroupAnimation調(diào)整duration和repeatCount/repeatDuration屬性并不會(huì)影響到子動(dòng)畫(huà)。但是beginTime仔蝌,timeOffset和speed屬性將會(huì)影響到子動(dòng)畫(huà)荒吏。然而在層級(jí)關(guān)系中,beginTime指定了父圖層開(kāi)始動(dòng)畫(huà)(或者組合關(guān)系中的父動(dòng)畫(huà))和對(duì)象將要開(kāi)始自己動(dòng)畫(huà)之間的偏移瞧挤。類似的,調(diào)整CALayer和CAGroupAnimation的speed屬性將會(huì)對(duì)動(dòng)畫(huà)以及子動(dòng)畫(huà)速度應(yīng)用一個(gè)縮放的因子执俩。
全局時(shí)間和本地時(shí)間
CoreAnimation有一個(gè)全局時(shí)間的概念癌刽,也就是所謂的馬赫時(shí)間(“馬赫”實(shí)際上是iOS和Mac OS系統(tǒng)內(nèi)核的命名)。馬赫時(shí)間在設(shè)備上所有進(jìn)程都是全局的--但是在不同設(shè)備上并不是全局的--不過(guò)這已經(jīng)足夠?qū)?dòng)畫(huà)的參考點(diǎn)提供便利了宋税,你可以使用CACurrentMediaTime函數(shù)來(lái)訪問(wèn)馬赫時(shí)間:
CFTimeInterval?time?=?CACurrentMediaTime();
這個(gè)函數(shù)返回的值其實(shí)無(wú)關(guān)緊要(它返回了設(shè)備自從上次啟動(dòng)后的秒數(shù)讼油,并不是你所關(guān)心的),它真實(shí)的作用在于對(duì)動(dòng)畫(huà)的時(shí)間測(cè)量提供了一個(gè)相對(duì)值乏屯。注意當(dāng)設(shè)備休眠的時(shí)候馬赫時(shí)間會(huì)暫停瘦赫,也就是所有的CAAnimations(基于馬赫時(shí)間)同樣也會(huì)暫停。
因此馬赫時(shí)間對(duì)長(zhǎng)時(shí)間測(cè)量并不有用含友。比如用CACurrentMediaTime去更新一個(gè)實(shí)時(shí)鬧鐘并不明智校辩。(可以用[NSDate date]代替,就像第三章例子所示)惠赫。
每個(gè)CALayer和CAAnimation實(shí)例都有自己本地時(shí)間的概念故黑,是根據(jù)父圖層/動(dòng)畫(huà)層級(jí)關(guān)系中的beginTime,timeOffset和speed屬性計(jì)算混埠。就和轉(zhuǎn)換不同圖層之間坐標(biāo)關(guān)系一樣诗轻,CALayer同樣也提供了方法來(lái)轉(zhuǎn)換不同圖層之間的本地時(shí)間。如下:
-?(CFTimeInterval)convertTime:(CFTimeInterval)t?fromLayer:(CALayer?*)l;
-?(CFTimeInterval)convertTime:(CFTimeInterval)t?toLayer:(CALayer?*)l;
當(dāng)用來(lái)同步不同圖層之間有不同的speed使套,timeOffset和beginTime的動(dòng)畫(huà),這些方法會(huì)很有用侦高。
暫停奉呛,倒回和快進(jìn)
設(shè)置動(dòng)畫(huà)的speed屬性為0可以暫停動(dòng)畫(huà),但在動(dòng)畫(huà)被添加到圖層之后不太可能再修改它了瞧壮,所以不能對(duì)正在進(jìn)行的動(dòng)畫(huà)使用這個(gè)屬性咆槽。給圖層添加一個(gè)CAAnimation實(shí)際上是給動(dòng)畫(huà)對(duì)象做了一個(gè)不可改變的拷貝,所以對(duì)原始動(dòng)畫(huà)對(duì)象屬性的改變對(duì)真實(shí)的動(dòng)畫(huà)并沒(méi)有作用秦忿。相反灯谣,直接用-animationForKey:來(lái)檢索圖層正在進(jìn)行的動(dòng)畫(huà)可以返回正確的動(dòng)畫(huà)對(duì)象,但是修改它的屬性將會(huì)拋出異常胎许。
如果移除圖層正在進(jìn)行的動(dòng)畫(huà)辜窑,圖層將會(huì)急速返回動(dòng)畫(huà)之前的狀態(tài)。但如果在動(dòng)畫(huà)移除之前拷貝呈現(xiàn)圖層到模型圖層谬擦,動(dòng)畫(huà)將會(huì)看起來(lái)暫停在那里惨远。但是不好的地方在于之后就不能再恢復(fù)動(dòng)畫(huà)了话肖。
一個(gè)簡(jiǎn)單的方法是可以利用CAMediaTiming來(lái)暫停圖層本身。如果把圖層的speed設(shè)置成0最筒,它會(huì)暫停任何添加到圖層上的動(dòng)畫(huà)床蜘。類似的蔑水,設(shè)置speed大于1.0將會(huì)快進(jìn)扬蕊,設(shè)置成一個(gè)負(fù)值將會(huì)倒回動(dòng)畫(huà)。
通過(guò)增加主窗口圖層的speed歇父,可以暫停整個(gè)應(yīng)用程序的動(dòng)畫(huà)再愈。這對(duì)UI自動(dòng)化提供了好處,我們可以加速所有的視圖動(dòng)畫(huà)來(lái)進(jìn)行自動(dòng)化測(cè)試(注意對(duì)于在主窗口之外的視圖并不會(huì)被影響垂睬,比如UIAlertview)抗悍。可以在app delegate設(shè)置如下進(jìn)行驗(yàn)證:
self.window.layer.speed?=?100;
你也可以通過(guò)這種方式來(lái)減速逻淌,但其實(shí)也可以在模擬器通過(guò)切換慢速動(dòng)畫(huà)來(lái)實(shí)現(xiàn)疟暖。
手動(dòng)動(dòng)畫(huà)
timeOffset一個(gè)很有用的功能在于你可以它可以讓你手動(dòng)控制動(dòng)畫(huà)進(jìn)程,通過(guò)設(shè)置speed為0骨望,可以禁用動(dòng)畫(huà)的自動(dòng)播放欣舵,然后來(lái)使用timeOffset來(lái)來(lái)回顯示動(dòng)畫(huà)序列。這可以使得運(yùn)用手勢(shì)來(lái)手動(dòng)控制動(dòng)畫(huà)變得很簡(jiǎn)單劣光。
舉個(gè)簡(jiǎn)單的例子:還是之前關(guān)門(mén)的動(dòng)畫(huà)糟把,修改代碼來(lái)用手勢(shì)控制動(dòng)畫(huà)。我們給視圖添加一個(gè)UIPanGestureRecognizer雄可,然后用timeOffset左右搖晃。
因?yàn)樵趧?dòng)畫(huà)添加到圖層之后不能再做修改了聪舒,我們來(lái)通過(guò)調(diào)整layer的timeOffset達(dá)到同樣的效果(清單9.4)虐急。
清單9.4 通過(guò)觸摸手勢(shì)手動(dòng)控制動(dòng)畫(huà)
@interface?ViewController?()
@property?(nonatomic,?weak)?UIView?*containerView;
@property?(nonatomic,?strong)?CALayer?*doorLayer;
@end
@implementation?ViewController
-?(void)viewDidLoad
{
[super?viewDidLoad];
//add?the?door
self.doorLayer?=?[CALayer?layer];
self.doorLayer.frame?=?CGRectMake(0,?0,?128,?256);
self.doorLayer.position?=?CGPointMake(150?-?64,?150);
self.doorLayer.anchorPoint?=?CGPointMake(0,?0.5);
self.doorLayer.contents?=?(__bridge?id)[UIImage?imageNamed:@"Door.png"].CGImage;
[self.containerView.layer?addSublayer:self.doorLayer];
//apply?perspective?transform
CATransform3D?perspective?=?CATransform3DIdentity;
perspective.m34?=?-1.0?/?500.0;
self.containerView.layer.sublayerTransform?=?perspective;
//add?pan?gesture?recognizer?to?handle?swipes
UIPanGestureRecognizer?*pan?=?[[UIPanGestureRecognizer?alloc]?init];
[pan?addTarget:self?action:@selector(pan:)];
[self.view?addGestureRecognizer:pan];
//pause?all?layer?animations
self.doorLayer.speed?=?0.0;
//apply?swinging?animation?(which?won't?play?because?layer?is?paused)
CABasicAnimation?*animation?=?[CABasicAnimation?animation];
animation.keyPath?=?@"transform.rotation.y";
animation.toValue?=?@(-M_PI_2);
animation.duration?=?1.0;
[self.doorLayer?addAnimation:animation?forKey:nil];
}
-?(void)pan:(UIPanGestureRecognizer?*)pan
{
//get?horizontal?component?of?pan?gesture
CGFloat?x?=?[pan?translationInView:self.view].x;
//convert?from?points?to?animation?duration?//using?a?reasonable?scale?factor
x?/=?200.0f;
//update?timeOffset?and?clamp?result
CFTimeInterval?timeOffset?=?self.doorLayer.timeOffset;
timeOffset?=?MIN(0.999,?MAX(0.0,?timeOffset?-?x));
self.doorLayer.timeOffset?=?timeOffset;
//reset?pan?gesture
[pan?setTranslation:CGPointZero?inView:self.view];
}
@end
這其實(shí)是個(gè)小詭計(jì)戏仓,也許相對(duì)于設(shè)置個(gè)動(dòng)畫(huà)然后每次顯示一幀而言,用移動(dòng)手勢(shì)來(lái)直接設(shè)置門(mén)的transform會(huì)更簡(jiǎn)單敷待。
在這個(gè)例子中的確是這樣仁热,但是對(duì)于比如說(shuō)關(guān)鍵這這樣更加復(fù)雜的情況,或者有多個(gè)圖層的動(dòng)畫(huà)組举哟,相對(duì)于實(shí)時(shí)計(jì)算每個(gè)圖層的屬性而言迅矛,這就顯得方便的多了。
總結(jié)
在這一章壶硅,我們了解了CAMediaTiming協(xié)議销斟,以及Core Animation用來(lái)操作時(shí)間控制動(dòng)畫(huà)的機(jī)制。在下一章约谈,我們將要接觸緩沖犁钟,另一個(gè)用來(lái)使動(dòng)畫(huà)更加真實(shí)的操作時(shí)間的技術(shù)。
--------------------------------------------------------------------------------------------------------------------------------------------------------