Как отключить неявную анимацию CALayer? - PullRequest
36 голосов
/ 29 апреля 2011

Это сводит меня с ума!Я работаю над приложением для рисования.Допустим, я работаю над листом UIView.

Я добавляю несколько подслоев в это представление ([sheet.layer addSublayer:...]) и затем хочу нарисовать их.Для этого я создаю CGImageRef и помещаю его в слой contents.Но он анимированный, и я не хочу этого.

Я попробовал все:

  • removeAnimationForKey:
  • removeAllAnimations
  • установитьсловарь действий
  • с использованием actionlayer delegate
  • [CATransaction setDisableAnimations:YES]

Это кажется правильным.Я не понимаю, почему этот слой все еще анимирован; _;
Я что-то не так делаю?Есть ли секретный способ?

Ответы [ 10 ]

37 голосов
/ 02 мая 2011

Вы должны явно отключить анимацию, поместив свой код в транзакцию CAT

[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue
                 forKey:kCATransactionDisableActions];
layer.content = someImageRef;
[CATransaction commit];
28 голосов
/ 22 января 2016

Swift

CATransaction.begin()
CATransaction.setDisableActions(true)

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

CATransaction.commit()
15 голосов
/ 22 февраля 2015

Начиная с 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 голосов
/ 03 ноября 2011

Другой способ:

  1. Вы должны отключить стандартную анимацию вашего sheet.layer, которая вызывается неявно при добавлении подслоя.

  2. Вы также должны контент-анимация каждого подслоя. Конечно, вы можете использовать "kCATransactionDisableActions" CATransaction каждый раз, когда вы устанавливаете sublayer.content. Но вы можете отключить эту анимацию один раз при создании подслоя.


Вот код:

// 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.       
1 голос
/ 20 февраля 2019

Расширение 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 голос
/ 22 апреля 2017

Повторно используемый глобальный код:

/**
 * 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 голос
/ 11 октября 2016

Swift 2

Мне удалось отключить все анимации следующим образом, где myView - это вид, с которым вы работаете:

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

И какпримечание стороны, удалив все слои:

myView.layer.sublayers?.forEach { $0.removeFromSuperlayer() }
0 голосов
/ 11 июля 2019

Я полностью согласен с Райаном. Его ответ - для MacOS, для iOS вы добавляете следующее, чтобы создать действие NSNull (). Этот вопрос Отключение неявной анимации в - [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)
}
0 голосов
/ 12 февраля 2019

Это старый вопрос, но проблема остается.Иногда вам не нужны анимации, которые навязывает вам 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 голосов
/ 01 октября 2018

до добавление слоя к вашему виду с помощью, например, [self.layer addSublayer:yourCALayer], а также после того, как он уже добавлен, вы можете отключить определенные анимированные свойства вашего CALayer, перезаписав ключ анимации.Ключ, который вы установили в NULL, назван в честь свойства, здесь показано, как это было сделано для layer.position = CGPoint(x,y);

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

Поскольку свойство actions является NSDictionary, который не позволяет хранить nil васустановить явное значение для объекта NULL с помощью [NSNull 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"]];
}
...