如何禁用CALayer隐式动画?

问题描述 投票:36回答:9

这让我疯狂!我正在制作绘图应用程序。假设我正在制作一张叫做表格的UIView

我在这个视图中添加了一些子图层([sheet.layer addSublayer:...])然后我想要绘制它们。为此我创建了一个CGImageRef并将其放入图层的contents中。但它是动画的,我不希望这样。

我尝试了一切:

  • removeAnimationForKey:
  • removeAllAnimations
  • 设置动作字典
  • 使用动作层delegate
  • [CATransaction setDisableAnimations:YES]

这似乎是正确的。我不明白为什么这个图层仍然是动画的; _; 难道我做错了什么?有秘密的方式吗?

ios animation calayer
9个回答
37
投票

您必须通过将代码包装在CATransaction中来显式禁用动画

[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue
                 forKey:kCATransactionDisableActions];
layer.content = someImageRef;
[CATransaction commit];

0
投票

我完全赞同瑞安。他的答案是MacOS,对于iOS,你添加以下内容来创建一个NSNull()动作。这个问题Disabling implicit animations in -[CALayer setNeedsDisplayInRect:]和标题中的文档引导我在这里

// Disable the implicit animation for changes to position
override open class func defaultAction(forKey event: String) -> CAAction? {
    if event == #keyPath(position) {
        return NSNull()
    }
    return super.defaultAction(forKey: event)
}

29
投票

迅速

CATransaction.begin()
CATransaction.setDisableActions(true)

// change layer properties that you don't want to animate

CATransaction.commit()

15
投票

从Mac OS X 10.6和iOS 3开始,CATransaction还有一个setDisableActions方法,用于设置关键kCATransactionDisableActions的值。

[CATransaction begin];
[CATransaction setDisableActions:YES];

layer.content = someImageRef;

[CATransaction commit];

在Swift中,我喜欢使用这种扩展方法:

extension CATransaction {
    class func withDisabledActions<T>(_ body: () throws -> T) rethrows -> T {
        CATransaction.begin()
        CATransaction.setDisableActions(true)
        defer {
            CATransaction.commit()
        }
        return try body()
    }
}

然后你可以像这样使用它:

CATransaction.withDisabledActions {
    // your stuff here
}

11
投票

其他方式:

  1. 您应该禁用sheet.layer的默认动画,在添加子图层时会隐式调用它。
  2. 您还应该对每个子图层进行内容动画处理。当然,每次设置sublayer.content时都可以使用CATransaction的“kCATransactionDisableActions”。但是,您可以在创建子图层时禁用此动画一次。

这是代码:

// disable animation of container
sheet.layer.actions = [NSDictionary dictionaryWithObject:[NSNull null] 
                                                  forKey:@"sublayers"];

// disable animation of each sublayer
sublayer.layer.actions = [NSDictionary dictionaryWithObject:[NSNull null] 
                                                     forKey:@"content"];

// maybe, you'll also have to disable "onOrderIn"-action of each sublayer.       

2
投票

Swift 4扩展:

extension CATransaction {

    static func disableAnimations(_ completion: () -> Void) {
        CATransaction.begin()
        CATransaction.setDisableActions(true)
        completion()
        CATransaction.commit()
    }

}

用法:

    CATransaction.disableAnimations {
        // things you don't want to animate
    }

1
投票

斯威夫特2

我可以按如下方式禁用所有动画,其中myView是您正在使用的视图:

myView.layer.sublayers?.forEach { $0.removeAllAnimations() }

作为旁注,删除所有图层:

myView.layer.sublayers?.forEach { $0.removeFromSuperlayer() }

1
投票

Reusable global code:

/**
 * Disable Implicit animation
 * EXAMPLE: disableAnim{view.layer?.position = 20}//Default animation is now disabled
 */
func disableAnim(_ closure:()->Void){
    CATransaction.begin()
    CATransaction.setDisableActions(true)
    closure()
    CATransaction.commit()
}

在代码中的任何位置添加此代码(全局范围)


1
投票

这是一个老问题,但问题仍然存在。有时候你不想要CALayer强加给你的动画。我对基于交易的方法不满意,因为我只想关闭这些操作。好的。这是一个Swift 4解决方案,用于子类CALayer,允许选择是允许任何操作还是全局禁用它们。您还可以使用相同的内容创建CAShapeLayer,CATextLayer子类:

public class ActionCALayer: CALayer {
    public var allowActions: Bool = false

    override public func action(forKey event: String) -> CAAction? {
        return allowActions ? super.action(forKey: event) : nil
    }
}

0
投票

在使用[self.layer addSublayer:yourCALayer]将图层添加到视图之前,并且在已添加图层之后,您可以通过覆盖动画密钥来禁用CALayer的特定动画属性。您设置为NULL的键以属性命名,此处显示为layer.position = CGPoint(x,y);的完成

yourCALayer.actions = [NSDictionary dictionaryWithObject:[NSNull null] forKey:@"position"];

因为actions属性是一个不允许存储nil的NSDictionary,所以你可以使用[NSNull null]将它显式设置为NULL对象,这与(id)kCFNull相同。你可以通过遍历视图层的所有子层来为所有子层执行此操作。 。

for (CALayer *iterationLayer in self.layer.sublayers ) {
    iterationLayer.actions = [NSDictionary dictionaryWithObject:[NSNull null] forKey:@"position"];
    //or for multiple keys at once
    NSNull *nop = [NSNull null];
    iterationLayer.actions = [NSDictionary dictionaryWithObjects:@[nop,nop] forKeys:@[@"position",@"contents"]];
}
© www.soinside.com 2019 - 2024. All rights reserved.