如何禁用CALayer隐式动画?

时间:2011-04-29 14:38:54

标签: ios animation calayer

这让我发疯了!我正在制作绘图应用程序。假设我正在处理一张名为UIView的表格。

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

我尝试了一切:

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

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

11 个答案:

答案 0 :(得分:37)

您必须通过将代码包装在CATransaction

中来显式禁用动画
[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue
                 forKey:kCATransactionDisableActions];
layer.content = someImageRef;
[CATransaction commit];

答案 1 :(得分:27)

<强>夫特

CATransaction.begin()
CATransaction.setDisableActions(true)

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

CATransaction.commit()

答案 2 :(得分: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
}

答案 3 :(得分:11)

另一种方式:

  1. 您应该禁用sheet.layer的默认动画,在添加子图层时会隐式调用它。

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


  3. 这是代码:

    // 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.       
    

答案 4 :(得分:1)

Swift 2

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

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

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

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

答案 5 :(得分:1)

可重复使用的全球代码:

/**
 * 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()
}

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

答案 6 :(得分:1)

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
    }

答案 7 :(得分:0)

之前,使用[self.layer addSublayer:yourCALayer]将图层添加到视图中,也可以在添加图层之后,通过覆盖动画键来禁用CALayer的特定动画属性。设置为NULL的键以该属性命名,如layer.position = CGPoint(x,y);

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

由于actions属性是一个NSDictionary,它不允许存储nil,因此您可以使用[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"]];
}

答案 8 :(得分:0)

这是一个古老的问题,但问题仍然存在。有时您不希望CALayer强制执行的动画。我对基于事务的方法不满意,因为我只想关闭这些操作。为了好这是CALayer子类的Swift 4解决方案,它允许选择是允许任何操作还是全局禁用它们。您还可以创建具有相同内容的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
    }
}

答案 9 :(得分: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)
}

答案 10 :(得分:0)

图层扩展:

extension CALayer {    
    var areAnimationsEnabled: Bool {
        set {
            delegate = newValue ? nil : CALayerAnimationsDelegate.shared
        }
        get {
            return delegate == nil
        }
    }
}

fileprivate class CALayerAnimationsDelegate: NSObject, CALayerDelegate {
    static let shared = CALayerAnimationsDelegate()
    private let null = NSNull()

    func action(for layer: CALayer, forKey event: String) -> CAAction? {
        return null
    }
}

用法:

anyLayer.areAnimationsEnabled = false