我正在旋转CALayer,并尝试在动画完成后将其停在其最终位置。
但是动画制作完成后,它将重置为初始位置。
(xcode文档明确表示动画将不会更新属性的值。)
任何建议如何实现这一目标。
我正在旋转CALayer,并尝试在动画完成后将其停在其最终位置。
但是动画制作完成后,它将重置为初始位置。
(xcode文档明确表示动画将不会更新属性的值。)
任何建议如何实现这一目标。
Answers:
这是答案,是我的答案和克里希南的答案的结合。
cabasicanimation.fillMode = kCAFillModeForwards;
cabasicanimation.removedOnCompletion = NO;
默认值为kCAFillModeRemoved
。(这是您看到的重置行为。)
removeOnCompletion的问题是UI元素不允许用户交互。
我的技术是在动画中设置FROM值,在对象上设置TO值。动画将在开始之前自动填充TO值,并且在删除动画时将使对象保持其正确状态。
// fade in
CABasicAnimation *alphaAnimation = [CABasicAnimation animationWithKeyPath: @"opacity"];
alphaAnimation.fillMode = kCAFillModeForwards;
alphaAnimation.fromValue = NUM_FLOAT(0);
self.view.layer.opacity = 1;
[self.view.layer addAnimation: alphaAnimation forKey: @"fade"];
alphaAnimation.beginTime = 1;
,fillMode
据我所知……是不需要的吗?
beginTime
不是相对的,您应该使用例如:CACurrentMediaTime()+1;
fillMode
,看多地防止这些层从回弹原始值的使用显式CAAnimations当
设置以下属性:
animationObject.removedOnCompletion = NO;
只需将其放入代码中
CAAnimationGroup *theGroup = [CAAnimationGroup animation];
theGroup.fillMode = kCAFillModeForwards;
theGroup.removedOnCompletion = NO;
将其添加到图层时,只需将其的键设置为CABasicAnimation
即可position
。这样,它将覆盖运行循环中当前通道位置上完成的隐式动画。
CGFloat yOffset = 30;
CGPoint endPosition = CGPointMake(someLayer.position.x,someLayer.position.y + yOffset);
someLayer.position = endPosition; // Implicit animation for position
CABasicAnimation * animation =[CABasicAnimation animationWithKeyPath:@"position.y"];
animation.fromValue = @(someLayer.position.y);
animation.toValue = @(someLayer.position.y + yOffset);
[someLayer addAnimation:animation forKey:@"position"]; // The explicit animation 'animation' override implicit animation
您可以了解有关2011 Apple WWDC Video Session 421-Core Animation Essentials(视频的中间部分)的更多信息。
someLayer.position = endPosition;
。并感谢您对WWDC的推荐!
CALayer具有模型层和表示层。在动画期间,表示层独立于模型进行更新。动画完成后,将使用模型中的值更新表示层。如果要避免动画结束后出现跳动,关键是使两个图层保持同步。
如果知道最终值,则可以直接设置模型。
self.view.layer.opacity = 1;
但是,如果您有一个不知道结束位置的动画(例如,用户可以暂停然后反转的缓慢淡入淡出),则可以直接查询表示层以找到当前值,然后更新模型。
NSNumber *opacity = [self.layer.presentationLayer valueForKeyPath:@"opacity"];
[self.layer setValue:opacity forKeyPath:@"opacity"];
从表示层提取值对于缩放或旋转关键路径也特别有用。(例如transform.scale
,transform.rotation
)
不使用 removedOnCompletion
您可以尝试以下技术:
self.animateOnX(item: shapeLayer)
func animateOnX(item:CAShapeLayer)
{
let endPostion = CGPoint(x: 200, y: 0)
let pathAnimation = CABasicAnimation(keyPath: "position")
//
pathAnimation.duration = 20
pathAnimation.fromValue = CGPoint(x: 0, y: 0)//comment this line and notice the difference
pathAnimation.toValue = endPostion
pathAnimation.fillMode = kCAFillModeBoth
item.position = endPostion//prevent the CABasicAnimation from resetting item's position when the animation finishes
item.add(pathAnimation, forKey: nil)
}
所以我的问题是我试图以平移手势旋转对象,因此每次移动都具有多个相同的动画。我既有fillMode = kCAFillModeForwards
,isRemovedOnCompletion = false
但无济于事。就我而言,每次添加新动画时,我必须确保动画键是不同的:
let angle = // here is my computed angle
let rotate = CABasicAnimation(keyPath: "transform.rotation.z")
rotate.toValue = angle
rotate.duration = 0.1
rotate.isRemovedOnCompletion = false
rotate.fillMode = CAMediaTimingFillMode.forwards
head.layer.add(rotate, forKey: "rotate\(angle)")
只是设置fillMode
而removedOnCompletion
对我不起作用。我通过将下面的所有属性设置为CABasicAnimation对象来解决了这个问题:
CABasicAnimation* ba = [CABasicAnimation animationWithKeyPath:@"transform"];
ba.duration = 0.38f;
ba.fillMode = kCAFillModeForwards;
ba.removedOnCompletion = NO;
ba.autoreverses = NO;
ba.repeatCount = 0;
ba.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.85f, 0.85f, 1.0f)];
[myView.layer addAnimation:ba forKey:nil];
此代码将转换myView
为其大小的85%(第3维不变)。
核心动画维护两层层次结构:模型层和表示层。进行动画时,模型层实际上是完整的,并保持其初始值。默认情况下,动画完成后将被删除。然后,表示层退回到模型层的值。
只需设置removedOnCompletion
为NO
,就不会删除动画并浪费内存。此外,模型层和表示层将不再同步,这可能会导致潜在的错误。
因此,将属性直接在模型层上更新为最终值将是一个更好的解决方案。
self.view.layer.opacity = 1;
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
animation.fromValue = 0;
animation.toValue = 1;
[self.view.layer addAnimation:animation forKey:nil];
如果上述代码的第一行引起了任何隐式动画,请尝试将其关闭:
[CATransaction begin];
[CATransaction setDisableActions:YES];
self.view.layer.opacity = 1;
[CATransaction commit];
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
animation.fromValue = 0;
animation.toValue = 1;
[self.view.layer addAnimation:animation forKey:nil];
这有效:
let animation = CABasicAnimation(keyPath: "opacity")
animation.fromValue = 0
animation.toValue = 1
animation.duration = 0.3
someLayer.opacity = 1 // important, this is the state you want visible after the animation finishes.
someLayer.addAnimation(animation, forKey: "myAnimation")
在动画过程中,核心动画在常规层的顶部显示一个“表示层”。因此,将不透明度(或其他值)设置为动画结束且表示层消失后要显示的颜色。在添加动画之前,请先在线进行此操作,以免动画完成后闪烁。
如果要延迟,请执行以下操作:
let animation = CABasicAnimation(keyPath: "opacity")
animation.fromValue = 0
animation.toValue = 1
animation.duration = 0.3
animation.beginTime = someLayer.convertTime(CACurrentMediaTime(), fromLayer: nil) + 1
animation.fillMode = kCAFillModeBackwards // So the opacity is 0 while the animation waits to start.
someLayer.opacity = 1 // <- important, this is the state you want visible after the animation finishes.
someLayer.addAnimation(animation, forKey: "myAnimation")
最后,如果使用“ removedOnCompletion = false”,它将泄漏CAAnimations,直到最终处置该层为止-避免。
@Leslie Godwin的回答不是很好,“ self.view.layer.opacity = 1;” 立即完成(大约需要一秒钟),如果有疑问,请将alphaAnimation.duration修复为10.0。您必须删除此行。
因此,当您将fillMode固定为kCAFillModeForwards,而将removeOnCompletion固定为NO时,则让动画保留在图层中。如果您修复了动画委托并尝试执行以下操作:
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
{
[theLayer removeAllAnimations];
}
...该层在您执行此行时立即恢复。这是我们想要避免的。
您必须先修复layer属性,然后再从中删除动画。试试这个:
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
{
if([anim isKindOfClass:[CABasicAnimation class] ]) // check, because of the cast
{
CALayer *theLayer = 0;
if(anim==[_b1 animationForKey:@"opacity"])
theLayer = _b1; // I have two layers
else
if(anim==[_b2 animationForKey:@"opacity"])
theLayer = _b2;
if(theLayer)
{
CGFloat toValue = [((CABasicAnimation*)anim).toValue floatValue];
[theLayer setOpacity:toValue];
[theLayer removeAllAnimations];
}
}
}
似乎removeOnCompletion标志设置为false且fillMode设置为kCAFillModeForwards对我也不起作用。
在图层上应用新动画后,动画对象将重置为其初始状态,然后从该状态进行动画处理。另外要做的是在设置新动画之前,根据其表示层的属性设置模型层的所需属性:
someLayer.path = ((CAShapeLayer *)[someLayer presentationLayer]).path;
[someLayer addAnimation:someAnimation forKey:@"someAnimation"];
最简单的解决方案是使用隐式动画。这将为您解决所有麻烦:
self.layer?.backgroundColor = NSColor.red.cgColor;
如果要自定义持续时间,则可以使用NSAnimationContext
:
NSAnimationContext.beginGrouping();
NSAnimationContext.current.duration = 0.5;
self.layer?.backgroundColor = NSColor.red.cgColor;
NSAnimationContext.endGrouping();
注意:这仅在macOS上进行了测试。
开始时,我最初看不到任何动画。的问题是,一个视图支持层的层确实不隐动画。要解决此问题,请确保自己添加一个图层(在将视图设置为支持图层之前)。
如何做到这一点的一个例子是:
override func awakeFromNib() {
self.layer = CALayer();
//self.wantsLayer = true;
}
使用self.wantsLayer
对我的测试没有任何影响,但是可能会有一些我不知道的副作用。
这是操场的一个例子:
import PlaygroundSupport
import UIKit
let resultRotation = CGFloat.pi / 2
let view = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 200.0, height: 300.0))
view.backgroundColor = .red
//--------------------------------------------------------------------------------
let rotate = CABasicAnimation(keyPath: "transform.rotation.z") // 1
rotate.fromValue = CGFloat.pi / 3 // 2
rotate.toValue = resultRotation // 3
rotate.duration = 5.0 // 4
rotate.beginTime = CACurrentMediaTime() + 1.0 // 5
// rotate.isRemovedOnCompletion = false // 6
rotate.fillMode = .backwards // 7
view.layer.add(rotate, forKey: nil) // 8
view.layer.setAffineTransform(CGAffineTransform(rotationAngle: resultRotation)) // 9
//--------------------------------------------------------------------------------
PlaygroundPage.current.liveView = view
false
为isRemovedOnCompletion
-动画完成后让Core Animation清理
.presentation()
即可获得“最终看到的”价值。在下面搜索正确的答案,以解释它是如何通过表示层完成的。