iOS-Core-Animation-Advanced-Techniques(九:時(shí)間圖層)

本文轉(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ù)。

--------------------------------------------------------------------------------------------------------------------------------------------------------

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末军俊,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌镰官,老刑警劉巖吗货,帶你破解...
    沈念sama閱讀 210,914評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件宙搬,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡勇垛,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評(píng)論 2 383
  • 文/潘曉璐 我一進(jìn)店門(mén)谆级,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)肥照,“玉大人勤众,你說(shuō)我怎么就攤上這事∫谡簦” “怎么了掌桩?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,531評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)茅坛。 經(jīng)常有香客問(wèn)我则拷,道長(zhǎng),這世上最難降的妖魔是什么斥铺? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,309評(píng)論 1 282
  • 正文 為了忘掉前任坛善,我火速辦了婚禮,結(jié)果婚禮上剔交,老公的妹妹穿的比我還像新娘。我一直安慰自己驯镊,他們只是感情好竭鞍,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著冯乘,像睡著了一般滨砍。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上领追,一...
    開(kāi)封第一講書(shū)人閱讀 49,730評(píng)論 1 289
  • 那天绒窑,我揣著相機(jī)與錄音舔亭,去河邊找鬼。 笑死钦铺,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的洼哎。 我是一名探鬼主播沼本,決...
    沈念sama閱讀 38,882評(píng)論 3 404
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼抽兆,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了凭涂?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,643評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤较幌,失蹤者是張志新(化名)和其女友劉穎白翻,沒(méi)想到半個(gè)月后绢片,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,095評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡巢株,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評(píng)論 2 325
  • 正文 我和宋清朗相戀三年阁苞,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了祠挫。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,566評(píng)論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡骚灸,死狀恐怖慌植,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情丈钙,我是刑警寧澤交汤,帶...
    沈念sama閱讀 34,253評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站喉誊,受9級(jí)特大地震影響纵顾,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜敷矫,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望榨汤。 院中可真熱鬧怎茫,春花似錦、人聲如沸轨蛤。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,715評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)澳窑。三九已至供常,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間话侧,已是汗流浹背瞻鹏。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,945評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留薪夕,地道東北人赫悄。 一個(gè)月前我還...
    沈念sama閱讀 46,248評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像姑隅,于是被迫代替她去往敵國(guó)和親倔撞。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容