簡體   English   中英

理解暫停和恢復圖層上的動畫

[英]Comprehend pause and resume animation on a layer

我正在研究核心動畫編程指南中的動畫,我不得不理解暫停和恢復圖層上的動畫。

該文檔告訴我如何在沒有明確解釋的情況下暫停和恢復動畫。 我認為關鍵是要了解什么是timeOffsetbeginTime的方法CAlayer

這些代碼是暫停和恢復動畫。 resumeLayer方法中, layer.beginTime = timeSincePause; 這條線真讓我困惑。

-(void)pauseLayer:(CALayer*)layer {
   CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
   layer.speed = 0.0;
   layer.timeOffset = pausedTime;
}

-(void)resumeLayer:(CALayer*)layer {
   CFTimeInterval pausedTime = [layer timeOffset];
   layer.speed = 1.0;
   layer.timeOffset = 0.0;
   layer.beginTime = 0.0;
   CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
   layer.beginTime = timeSincePause;
}

任何幫助將不勝感激。

讓我們對圖層的兩個屬性進行測試: beginTimetimeOffset

條件

我們使用[layer convertTime:CACurrentMediaTime() fromLayer:nil]獲得CALayer的時間空間

1,將5.0分配給圖層的beginTime (之前的beginTime為0):

NSLog(@"CACurrentMediaTime:%f", [t1.layer convertTime:CACurrentMediaTime() fromLayer:nil]) ;
t1.layer.beginTime = 5.0 ;
NSLog(@"CACurrentMediaTime:%f",[t1.layer convertTime:CACurrentMediaTime() fromLayer:nil]) ;

結果日志是:

2014-01-15 11:00:33.811 newUserInterface[1404:70b] CACurrentMediaTime:7206.884498
2014-01-15 11:00:33.811 newUserInterface[1404:70b] CACurrentMediaTime:7201.885088

結果顯示,如果我在beginTime上添加5.0,則圖層的時間將減去5.0。 如果動畫beginTime則在beginTime上添加5.0將導致動畫在5.0秒前重做動畫。

2,將5.0分配給圖層的timeOffsettimeOffset為0之前):

NSLog(@"CACurrentMediaTime:%f", [t1.layer convertTime:CACurrentMediaTime() fromLayer:nil]) ;
t1.layer.timeOffset = 5.0 ;
NSLog(@"CACurrentMediaTime:%f",[t1.layer convertTime:CACurrentMediaTime() fromLayer:nil]) ;

結果是:

2014-01-15 11:09:07.757 newUserInterface[1449:70b] CACurrentMediaTime:7720.851464
2014-01-15 11:09:07.758 newUserInterface[1449:70b] CACurrentMediaTime:7725.852011

結果顯示,如果我在timeOffset上添加5.0,則圖層的時間將增加5.0。 如果動畫正在進行中,則在timeOffset上添加5.0將導致動畫跳轉到5.0秒后的動畫。

理解暫停和恢復圖層上的動畫

這是一個例子, t1是UIViewController的根視圖的子視圖。 我在t1上做了一個動畫,它動畫了t1.layer的位置。

如果將動畫添加到圖層,則圖層將根據動畫的beginTime計算何時為動畫設置動畫,如果beginTime為0,則會立即為其設置動畫。

CABasicAnimation * b1 = [CABasicAnimation animationWithKeyPath:@"position"] ;
b1.toValue = [NSValue valueWithCGPoint:CGPointMake(160.0, 320.0)] ;
b1.duration = 10.0f ;
[t1.layer addAnimation:b1 forKey:@"pos"] ;
NSLog(@"CACurrentMediaTime:%f", [t1.layer convertTime:CACurrentMediaTime() fromLayer:nil]) ;

log shows 2014-01-15 11:25:53.975 newUserInterface[1530:70b] CACurrentMediaTime:8727.108740 ,這意味着動畫將從8727開始,並在t1.layer的時空中停在8727 + 10。

當動畫在飛行中時,我使用- (void)pauseLayer:(CALayer*)layer方法暫停動畫。

layer.speed = 0.0; 將導致圖層停止,圖層的時間將設置為0.(我知道,因為當將layer.speed設置為0時,我會立即獲取圖層的時間並記錄它)

layer.timeOffset = pausedTime; 將pauseTime添加到圖層的時間(假設layer.timeOffset為0),現在圖層的時間為pausedTime。

- (void)pauseLayer:(CALayer *)layer
{
    CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil]; // pauseTime is the time with respect to layer's time space
    layer.speed = 0.0; // layer's local time is 0
    layer.timeOffset = pausedTime; // layer's local time is pausedTime, so animation stop here
}

然后我將使用- (void)resumeLayer:(CALayer*)layer方法恢復動畫。

-(void)resumeLayer:(CALayer*)layer {
   CFTimeInterval pausedTime = [layer timeOffset];
   layer.speed = 1.0;
   layer.timeOffset = 0.0;
   layer.beginTime = 0.0;
   CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
   layer.beginTime = timeSincePause;
}

如果我將動畫停在8727 + 1(意味着動畫動畫為1秒),則在pauseLayer方法中, layer.speed = 0會將圖層的時間設置為0並且layer.timeOffset = pausedTime; 將在圖層的時間上添加pausedTime,因此圖層的時間為pausedTime。

等一下,我們現在有一個摘要。 layer.speed為0.0,'layer.timeOffset'等於pausedTime ,即8727 + 1,圖層的時間也是pausedTime。 請記住,我們會盡快使用它們。

讓我們繼續,我用resumeLayer方法恢復8727 + 11的動畫, layer.speed = 1.0; 它將在圖層的時間上添加8727 + 11,因此圖層的時間為8727 + 1 + 8727 + 11, layer.timeOffset = 0.0; 它導致圖層的時間減去8727 + 1,因為layer.timeOffset之前是8727 + 1,圖層的本地時間現在是8727 + 11。 timeSincePause是(8727 + 11-8727-1)= 10。

layer.beginTime = timeSincePause; 它導致圖層的時間減去10.現在圖層的本地時間是8727 + 1,這是我暫停動畫的時間。

我將向您展示代碼和日志:

- (void)pauseLayer:(CALayer *)layer
{
    NSLog(@"%f", CACurrentMediaTime()) ;
    NSLog(@"pauseLayer begin:%f", [t1.layer convertTime:CACurrentMediaTime() fromLayer:nil]) ;
    CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil] ;
    layer.speed = 0.0 ;
    NSLog(@"pauseLayer after set speed to 0:%f",[t1.layer convertTime:CACurrentMediaTime() fromLayer:nil]) ;
    layer.timeOffset = pausedTime ;
    NSLog(@"pauseLayer after set timeOffset:%f",[t1.layer convertTime:CACurrentMediaTime() fromLayer:nil]) ;
}

- (void)resumeLayer:(CALayer *)layer
{
    NSLog(@"%f", CACurrentMediaTime()) ;
    NSLog(@"resumeLayer begin:%f",[t1.layer convertTime:CACurrentMediaTime() fromLayer:nil]) ;
    CFTimeInterval pausedTime = layer.timeOffset ;
    layer.speed = 1.0 ;
    NSLog(@"resumeLayer after set speed to 1:%f",[t1.layer convertTime:CACurrentMediaTime() fromLayer:nil]) ;
    layer.timeOffset = 0.0;
    NSLog(@"resumeLayer after set timeOffset to 0:%f",[t1.layer convertTime:CACurrentMediaTime() fromLayer:nil]) ;
    layer.beginTime = 0.0 ;
    NSLog(@"resumeLayer after set beginTime to 0:%f",[t1.layer convertTime:CACurrentMediaTime() fromLayer:nil]) ;
    CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime ;
    layer.beginTime = timeSincePause ;
    NSLog(@"resumeLayer after set beginTime to timeSincePause:%f",[t1.layer convertTime:CACurrentMediaTime() fromLayer:nil]) ;
}

日志:

2014-01-15 13:14:34.157 newUserInterface[1762:70b] 15247.550325
2014-01-15 13:14:34.158 newUserInterface[1762:70b] pauseLayer begin:15247.550826
2014-01-15 13:14:34.158 newUserInterface[1762:70b] pauseLayer after set speed to 0:0.000000
2014-01-15 13:14:34.159 newUserInterface[1762:70b] pauseLayer after set timeOffset:15247.551284

2014-01-15 13:14:40.557 newUserInterface[1762:70b] 15253.950505
2014-01-15 13:14:40.558 newUserInterface[1762:70b] resumeLayer begin:15247.551284
2014-01-15 13:14:40.558 newUserInterface[1762:70b] resumeLayer after set speed to 1:30501.502810
2014-01-15 13:14:40.559 newUserInterface[1762:70b] resumeLayer after set timeOffset to 0:15253.952031
2014-01-15 13:14:40.559 newUserInterface[1762:70b] resumeLayer after set beginTime to 0:15253.952523
2014-01-15 13:14:40.560 newUserInterface[1762:70b] resumeLayer after set beginTime to timeSincePause:15247.551294

resumeLayer問題是resumeLayer方法:為什么不將兩個賦值行合並到一個layer.beginTime = timeSincePause; ,原因是在[layer convertTime:CACurrentMediaTime() fromLayer:nil] ,結果值與layer.beginTime有關。

   layer.beginTime = 0.0;
   CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
   layer.beginTime = timeSincePause;

說實話,我仍然不知道動畫是如何工作的,我正在做的是分析結果,這不是一個好的解決方案。 我很高興任何有這方面想法的人都可以分享。 謝謝!

在CAMediaTiming的頭文件中,我們可以看到這些代碼:

/* The begin time of the object, in relation to its parent object, if
 * applicable. Defaults to 0. */

@property CFTimeInterval beginTime;

/* The basic duration of the object. Defaults to 0. */

@property CFTimeInterval duration;

/* The rate of the layer. Used to scale parent time to local time, e.g.
 * if rate is 2, local time progresses twice as fast as parent time.
 * Defaults to 1. */

@property float speed;

/* Additional offset in active local time. i.e. to convert from parent
 * time tp to active local time t: t = (tp - begin) * speed + offset.
 * One use of this is to "pause" a layer by setting `speed' to zero and
 * `offset' to a suitable value. Defaults to 0. */

@property CFTimeInterval timeOffset;

公式有多重要:

t =(tp - 開始)*速度+偏移量

此公式定義全局時間(或父時間,tp)如何映射到圖層的本地時間。 這個公式可以解釋列出的代碼的所有內容:

  1. 在時間A,動畫暫停。 設置speed = 0和timeOffset = pauseTime后,圖層的本地時間等於pauseTime。 因為速度= 0,本地時間不會再增加;
  2. 在時間B,動畫恢復。 設置速度= 1.0,timeOffset = 0,beginTime = 0后,圖層的本地時間等於全局時間(或tp),即(timePause + timeSinacePause)。 但是我們需要動畫從時間點#A開始,所以我們設置beginTime = timeSincePaused,然后圖層的本地時間等於timePause。 當然,這導致動畫從暫停的點繼續。

在此輸入圖像描述

來自文檔

BEGINTIME

beginTime指定接收器相對於其父對象的開始時間(如果適用)。 (需要)

下面是取自它的一個很好的解釋在這里

如果動畫在動畫組中,則beginTime是從其父對象(動畫組)的開頭開始的偏移量。 因此,如果動畫的beginTime為5,則在動畫組開始后5秒開始。

如果將動畫直接添加到圖層,則beginTime仍然是其父對象(圖層)開頭的偏移量。 但是由於圖層的開始是在past1中,我不能簡單地將beginTime設置為5來延遲動​​畫5秒,因為圖層開始后5秒可能仍然是過去的時間。 我通常真正想要的是相對於動畫添加到圖層時的延遲 - 由addTime表示。

這是添加日志的問題代碼:

- (IBAction)start:(UIButton *)sender
{    
    [UIView animateWithDuration:10 animations:^() {//Move square to x=300
    }completion:^(BOOL finished){}];
}

- (IBAction)pause:(UIButton *)sender
{
    CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
    layer.speed = 0.0;
    layer.timeOffset = pausedTime;
    NSLog(@"pausedTime: %f",pausedTime);
}

- (IBAction)resume:(UIButton *)sender
{
    CFTimeInterval pausedTime = [layer timeOffset];
    layer.speed = 1.0;
    layer.timeOffset = 0.0;
    layer.beginTime = 0.0;
    NSLog(@"CACurrentMediaTime: %f",[layer convertTime:CACurrentMediaTime() fromLayer:nil]);
    CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
    NSLog(@"timeSincePause: %f",timeSincePause);
    layer.beginTime = timeSincePause;
}

輸出:

pausedTime: 20000
CACurrentMediaTime: 20005
timeSincePause: 5 // <- that's your begin time. When you hit resume you want to begin the animation from that relative time.

總而言之,

動畫持續時間總共為10,我將動畫停在5,我還希望它在恢復動畫時成為我的beginTime。

因此,我保存暫停時間並從當前時間中減去它,以獲得已經過去的相對動畫時間。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM