理解暂停和恢复图层上的动画

时间:2014-01-06 09:10:09

标签: ios iphone animation core-animation calayer

我正在研究核心动画编程指南中的动画,我不得不理解暂停和恢复图层上的动画。

该文档告诉我如何在没有明确说明的情况下暂停和恢复动画。我认为关键是要了解timeOffset的{​​{1}}和beginTime方法是什么。

这些代码是暂停和恢复动画。在CAlayer方法中,resumeLayer这一行确实让我感到困惑。

layer.beginTime = timeSincePause;

任何帮助将不胜感激。

3 个答案:

答案 0 :(得分:22)

让我们对图层的两个属性进行测试:beginTimetimeOffset

<强>前提条件

我们使用[layer convertTime:CACurrentMediaTime() fromLayer:nil]

获取CALayer的时间空间

1,将5.0分配给图层的beginTimebeginTime之前为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上添加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]) ;

日志显示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在图层的时间,所以图层的时间是暂停时间。

等一下,我们现在有一个摘要。 layer.speed为0.0,'layer.timeOffset'等于pausedTime,即8727 + 1,图层的时间也是pausedTime。请记住,我们会尽快使用它们。

让我们继续,我使用resumeLayer方法恢复8727 + 11的动画,layer.speed = 1.0;它将在图层的时间上添加8727 + 11,因此图层的时间是8727 + 1 + 8727 + 11,{ {1}}它导致图层的时间减去8727 + 1,因为layer.timeOffset之前是8727 + 1,图层的本地时间现在是8727 + 11。 layer.timeOffset = 0.0;是(8727 + 11-8727-1)= 10.

timeSincePause它导致图层的时间减去10.现在图层的本地时间是8727 + 1,这是我暂停动画的时间。

我会告诉你代码和日志:

layer.beginTime = timeSincePause;

日志:

- (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,原因是layer.beginTime = timeSincePause;,结果值与layer.beginTime有关。

[layer convertTime:CACurrentMediaTime() fromLayer:nil]

说实话,我仍然不知道动画是如何工作的,我正在做的是分析结果,这不是一个好的解决方案。我很高兴任何有这方面想法的人都可以分享。谢谢!

答案 1 :(得分:10)

在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 - begin)* speed + offset

此公式定义全局时间(或父时间,tp)如何映射到图层的本地时间。这个公式可以解释所列代码的所有内容:

  1. 在时间A,动画暂停。设置speed = 0和timeOffset = pauseTime后,图层的本地时间等于pauseTime。并且当地时间不会再增加因为速度= 0;
  2. 在时间B,动画恢复。设置速度= 1.0,timeOffset = 0,beginTime = 0后,图层的本地时间等于全局时间(或tp),即(timePause + timeSinacePause)。但是我们需要动画从时间点#A开始,所以我们设置beginTime = timeSincePaused,然后图层的本地时间等于timePause。当然,这会导致动画从暂停点开始继续。
  3. enter image description here

答案 2 :(得分:0)

来自docs

  

<强> BEGINTIME

     

beginTime指定接收器相对于其的开始时间   父对象,如果适用的话。 (所需的)

以下是对here

的一个很好的解释
  

如果动画在动画组中,则beginTime是偏移量   它的父对象的开头 - 动画组。因此,如果   动画的beginTime是5,它开始后5秒   动画组开始。

     

如果动画直接添加到图层,则beginTime仍然是   偏离其父对象的开头 - 图层。但是由于   一个图层的开头是在past1中,我不能简单地设置   beginTime to 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。

所以,我保存暂停时间并从当前时间中减去它,以获得已经过去的相对动画时间。