11

我正在尝试使用 setBeginTime 将图层不透明度和位置的动画延迟 3 秒。我已经调用了层boxLayer。动画进展顺利,但是在前 3 秒内(该图层还没有显示),图层显示在其最终位置和不透明度。它不应该。组动画不能解决问题。有人可以帮忙吗?请参见下面的代码:

// Create an animation that will change the opacity of a layer
CABasicAnimation *fader = [CABasicAnimation animationWithKeyPath:@"opacity"];


// It will last 1 second and will be delayed by 3 seconds
[fader setDuration:1.0];
[fader setBeginTime:CACurrentMediaTime()+3.0];


// The layer's opacity will start at 0.0 (completely transparent)
[fader setFromValue:[NSNumber numberWithFloat:startOpacity]];

// And the layer will end at 1.0 (completely opaque)
[fader setToValue:[NSNumber numberWithFloat:endOpacity]];

// Add it to the layer
[boxLayer addAnimation:fader forKey:@"BigFade"];

// Maintain opacity to 1.0 JUST TO MAKE SURE IT DOES NOT GO BACK TO ORIGINAL OPACITY
[boxLayer setOpacity:endOpacity];


// Create an animation that will change the position of a layer
CABasicAnimation *mover = [CABasicAnimation animationWithKeyPath:@"position"];

// It will last 1 second and will be delayed by 3 seconds
[mover setDuration:1.0];
[mover setBeginTime:CACurrentMediaTime()+3.0];

// Setting starting position
[mover setFromValue:[NSValue valueWithCGPoint:CGPointMake(startX, startY)]];

// Setting ending position
[mover setToValue:[NSValue valueWithCGPoint:CGPointMake(endX, endY)]];

// Add it to the layer
[boxLayer addAnimation:mover forKey:@"BigMove"];

// Maintain the end position at 400.0 450.0 OTHERWISE IT IS GOING BACK TO ORIGINAL POSITION
[boxLayer setPosition:CGPointMake(endX, endY)];
4

3 回答 3

18

问题是您正在将boxLayer属性 ofposition和 of设置opacity为它们的最终值。你需要:

  1. boxLayer属性设置为它们的起始值,而不是它们的结束值(这就是为什么它从结束位置/不透明度开始......通常如果动画立即开始,这不是问题,但是因为你推迟了开始,使用结束位置是有问题的);

  2. 对于您的两个动画,您必须更改removedOnCompletiontoNOfillModeto kCAFillModeForwards(这是防止它在完成后恢复到原始位置的正确方法)。

因此:

// Create an animation that will change the opacity of a layer
CABasicAnimation *fader = [CABasicAnimation animationWithKeyPath:@"opacity"];

// It will last 1 second and will be delayed by 3 seconds
[fader setDuration:1.0];
[fader setBeginTime:CACurrentMediaTime()+3.0];

// The layer's opacity will start at 0.0 (completely transparent)
[fader setFromValue:[NSNumber numberWithFloat:startOpacity]];

// And the layer will end at 1.0 (completely opaque)
[fader setToValue:[NSNumber numberWithFloat:endOpacity]];

// MAKE SURE IT DOESN'T CHANGE OPACITY BACK TO STARTING VALUE    
[fader setRemovedOnCompletion:NO];
[fader setFillMode:kCAFillModeForwards];

// Add it to the layer
[boxLayer addAnimation:fader forKey:@"BigFade"];

// SET THE OPACITY TO THE STARTING VALUE
[boxLayer setOpacity:startOpacity];

// Create an animation that will change the position of a layer
CABasicAnimation *mover = [CABasicAnimation animationWithKeyPath:@"position"];

// It will last 1 second and will be delayed by 3 seconds
[mover setDuration:1.0];
[mover setBeginTime:CACurrentMediaTime()+3.0];

// Setting starting position
[mover setFromValue:[NSValue valueWithCGPoint:CGPointMake(startX, startY)]];

// Setting ending position
[mover setToValue:[NSValue valueWithCGPoint:CGPointMake(endX, endY)]];

// MAKE SURE IT DOESN'T MOVE BACK TO STARTING POSITION   
[mover setRemovedOnCompletion:NO];
[mover setFillMode:kCAFillModeForwards];

// Add it to the layer
[boxLayer addAnimation:mover forKey:@"BigMove"];

// SET THE POSITION TO THE STARTING POSITION
[boxLayer setPosition:CGPointMake(startX, startY)];

就个人而言,我认为您正在做很多工作,这些工作在视图上使用基于块的动画更容易完成(出于本演示的目的,我假设您boxLayer是一个CALayer名为 的控件box)。如果你这样做,你也不需要 Quartz 2D:

box.alpha = startOpacity;
box.frame = CGRectMake(startX, startY, box.frame.size.width, box.frame.size.height);

[UIView animateWithDuration:1.0
                      delay:3.0
                    options:0
                 animations:^{
                     box.alpha = endOpacity;
                     box.frame = CGRectMake(endX, endY, box.frame.size.width, box.frame.size.height);
                 }
                 completion:nil];
于 2013-01-30T07:47:43.443 回答
2

为了使用beginTime你应该对你的动画对象进行必要的配置并设置fillModekCAFillModeBackwards喜欢

zoomAnimation.fillMode = kCAFillModeBackwards;

这在 Apple 文档中说:

使用 beginTime 属性设置动画的开始时间。通常,动画在下一个更新周期开始。您可以使用 beginTime 参数将动画开始时间延迟几秒钟。将两个动画链接在一起的方法是将一个动画的开始时间设置为与另一个动画的结束时间相匹配。如果您延迟动画的开始,您可能还需要将 fillMode 属性设置为 kCAFillModeBackwards。这种填充模式会导致图层显示动画的起始值,即使图层树中的图层对象包含不同的值。如果没有这种填充模式,您会在动画开始执行之前看到跳转到最终值。其他填充模式也可用。

https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CoreAnimation_guide/AdvancedAnimationTricks/AdvancedAnimationTricks.html#//apple_ref/doc/uid/TP40004514-CH8-SW2

另外,根据 Rob 的回答:

对于您的两个动画,您必须将 removedOnCompletion 更改为 NO 并将 fillMode 更改为 kCAFillModeForwards (这是防止它在完成后恢复到原始位置的正确方法)。

这是一种有争议的说法,因为:

一旦动画被删除,表示层将回退到模型层的值,并且由于我们从未修改过该层的位置,所以我们的宇宙飞船会重新出现在它开始的位置。有两种方法可以处理这个问题:

第一种方法是直接在模型层更新属性。这是推荐的方法,因为它使动画完全可选。

或者,您可以通过将其 fillMode 属性设置为 kCAFillModeForwards 来告诉动画保持其最终状态,并通过将 removedOnCompletion 设置为 NO 来防止它被自动删除。但是,保持模型层和表示层同步是一种很好的做法,因此应该谨慎使用这种方法

来自https://www.objc.io/issues/12-animations/animations-explained/

于 2017-05-26T15:08:09.760 回答
1

这篇文章很好地解释了为什么你不应该使用 removeOnCompletion 和 fillMode https://www.objc.io/issues/12-animations/animations-explained/

在我的情况下,我正在为用作导航的视图层设置动画,但会延迟该视图内的反弹动画;我需要在图层上更新这两个位置,因为它可以被解除然后再次显示。动画完成后,使用 removedOnCompletion 将不会更新图层的值

我这样做的方式是更新 CATransaction 完成块中的图层

CATransaction.setCompletionBlock { 
     // update the layer value
 }

CATransaction.begin()

// setup and add your animation

CATransaction.commit()
于 2016-08-31T15:02:48.790 回答