Deshabilitando animaciones implícitas en –

Tengo una capa con un código de dibujo complejo en su método -drawInContext :. Estoy tratando de minimizar la cantidad de dibujos que necesito hacer, así que estoy usando -setNeedsDisplayInRect: para actualizar solo las partes modificadas. Esto está funcionando espléndidamente. Sin embargo, cuando el sistema de gráficos actualiza mi capa, está pasando de la imagen anterior a la nueva utilizando un fundido cruzado. Me gustaría que cambie al instante.

Intenté usar CATransaction para desactivar acciones y establecer la duración en cero, y tampoco funcionar. Aquí está el código que estoy usando:

[CATransaction begin]; [CATransaction setDisableActions: YES]; [self setNeedsDisplayInRect: rect]; [CATransaction commit]; 

¿Hay algún otro método en CATransaction que debería usar en su lugar (también probé -setValue: forKey: con kCATransactionDisableActions, mismo resultado).

Puede hacer esto configurando el diccionario de acciones en la capa para devolver [NSNull null] como una animación para la clave adecuada. Por ejemplo, yo uso

 NSDictionary *newActions = @{ @"onOrderIn": [NSNull null], @"onOrderOut": [NSNull null], @"sublayers": [NSNull null], @"contents": [NSNull null], @"bounds": [NSNull null] }; layer.actions = newActions; 

para desactivar las animaciones de fundido de entrada / salida al insertar o cambiar las subcapas dentro de una de mis capas, así como los cambios en el tamaño y el contenido de la capa. Creo que la clave de contents es la que está buscando para evitar el fundido cruzado en el dibujo actualizado.

También:

 [CATransaction begin]; [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions]; //foo [CATransaction commit]; 

Cuando cambia la propiedad de una capa, CA generalmente crea un objeto de transacción implícito para animar el cambio. Si no desea animar el cambio, puede desactivar las animaciones implícitas creando una transacción explícita y estableciendo su propiedad kCATransactionDisableActions en verdadero .

C objective

 [CATransaction begin]; [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions]; // change properties here without animation [CATransaction commit]; 

Rápido

 CATransaction.begin() CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions) // change properties here without animation CATransaction.commit() 

Además de la respuesta de Brad Larson : para las capas personalizadas (creadas por usted) puede usar la delegación en lugar de modificar el diccionario de actions la capa. Este enfoque es más dynamic y puede ser más eficiente. Y permite deshabilitar todas las animaciones implícitas sin tener que enumerar todas las claves animables.

Desafortunadamente, es imposible usar UIView s como delegates de capas personalizadas, ya que cada UIView ya es un delegado de su propia capa. Pero puedes usar una clase de ayuda simple como esta:

 @interface MyLayerDelegate : NSObject @property (nonatomic, assign) BOOL disableImplicitAnimations; @end @implementation MyLayerDelegate - (id)actionForLayer:(CALayer *)layer forKey:(NSString *)event { if (self.disableImplicitAnimations) return (id)[NSNull null]; // disable all implicit animations else return nil; // allow implicit animations // you can also test specific key names; for example, to disable bounds animation: // if ([event isEqualToString:@"bounds"]) return (id)[NSNull null]; } @end 

Uso (dentro de la vista):

 MyLayerDelegate *delegate = [[MyLayerDelegate alloc] init]; // assign to a strong property, because CALayer's "delegate" property is weak self.myLayerDelegate = delegate; self.myLayer = [CALayer layer]; self.myLayer.delegate = delegate; // ... self.myLayerDelegate.disableImplicitAnimations = YES; self.myLayer.position = (CGPoint){.x = 10, .y = 42}; // will not animate // ... self.myLayerDelegate.disableImplicitAnimations = NO; self.myLayer.position = (CGPoint){.x = 0, .y = 0}; // will animate 

A veces es conveniente tener el controlador de la vista como un delegado para las subcapas personalizadas de la vista; en este caso no hay necesidad de una clase auxiliar, puede implementar el actionForLayer:forKey: dentro del controlador.

Nota importante: no intente modificar el delegado de la capa subyacente de UIView (p. Ej., Para habilitar animaciones implícitas): sucederán cosas malas 🙂

Nota: si desea animar (no deshabilitar la animación de) la capa vuelve a dibujar, es inútil poner la [CALayer setNeedsDisplayInRect:] dentro de una CATransaction , porque el redibujado real puede (y probablemente ocurrirá) algunas veces más tarde. El buen enfoque es usar propiedades personalizadas, como se describe en esta respuesta .

Aquí hay una solución más eficiente, similar a la respuesta aceptada, pero para Swift . En algunos casos, será mejor que crear una transacción cada vez que modifique el valor, que es una cuestión de rendimiento, como otros mencionaron, por ejemplo, caso de uso común de arrastrar la posición de capa a 60 fps.

 // Disable implicit position animation. layer.actions = ["position": NSNull()] 

Consulte los documentos de Apple para ver cómo se resuelven las acciones de capa . La implementación del delegado omitirá un nivel más en la cascada, pero en mi caso fue demasiado desordenado debido a la advertencia sobre la necesidad de configurar al delegado a la UIView asociada .

Editar: actualizado gracias al comentario que señala que NSNull ajusta a CAAction .

Con base en la respuesta de Sam y las dificultades de Simon … agregue la referencia del delegado después de crear CSShapeLayer:

 CAShapeLayer *myLayer = [CAShapeLayer layer]; myLayer.delegate = self; // <- set delegate here, it's magic. 

... en otro lugar en el archivo "m" ...

Esencialmente el mismo que Sam sin la capacidad de alternar a través de la disposición de variable personalizada "disableImplicitAnimations". Más de un enfoque "rígido".

 - (id)actionForLayer:(CALayer *)layer forKey:(NSString *)event { // disable all implicit animations return (id)[NSNull null]; // allow implicit animations // return nil; // you can also test specific key names; for example, to disable bounds animation: // if ([event isEqualToString:@"bounds"]) return (id)[NSNull null]; } 

En realidad, no encontré ninguna de las respuestas para ser la correcta. El método que resuelve el problema para mí fue este:

 - (id)actionForKey:(NSString *)event { return nil; } 

Entonces puedes usar la lógica que sea, para deshabilitar una animación específica, pero como quería eliminarlos a todos, devolví cero.

Encontré un método más simple para deshabilitar la acción dentro de una CATransaction que internamente llama a setValue:forKey: para la clave kCATransactionDisableActions :

 [CATransaction setDisableActions:YES]; 

Rápido:

 CATransaction.setDisableActions(true) 

Agregue esto a su clase personalizada donde está implementando el método -drawRect (). Haga cambios en el código para satisfacer sus necesidades, para mí la ‘opacidad’ hizo el truco para detener la animación de fundido cruzado.

 -(id) actionForLayer:(CALayer *)layer forKey:(NSString *)key { NSLog(@"key: %@", key); if([key isEqualToString:@"opacity"]) { return (id)[NSNull null]; } return [super actionForLayer:layer forKey:key]; } 

Para deshabilitar las animaciones de capa implícitas en Swift

 CATransaction.setDisableActions(true) 

A partir de iOS 7 hay un método de conveniencia que hace justamente esto:

 [UIView performWithoutAnimation:^{ // apply changes }]; 

Para desactivar la animación molesta (borrosa) al cambiar la propiedad de cadena de un CATextLayer, puede hacer esto:

 class CANullAction: CAAction { private static let CA_ANIMATION_CONTENTS = "contents" @objc func runActionForKey(event: String, object anObject: AnyObject, arguments dict: [NSObject : AnyObject]?) { // Do nothing. } } 

y luego úselo de esa manera (no olvide configurar su CATextLayer correctamente, por ejemplo, la fuente correcta, etc.):

 caTextLayer.actions = [CANullAction.CA_ANIMATION_CONTENTS: CANullAction()] 

Puedes ver mi configuración completa de CATextLayer aquí:

 private let systemFont16 = UIFont.systemFontOfSize(16.0) caTextLayer = CATextLayer() caTextLayer.foregroundColor = UIColor.blackColor().CGColor caTextLayer.font = CGFontCreateWithFontName(systemFont16.fontName) caTextLayer.fontSize = systemFont16.pointSize caTextLayer.alignmentMode = kCAAlignmentCenter caTextLayer.drawsAsynchronously = false caTextLayer.actions = [CANullAction.CA_ANIMATION_CONTENTS: CANullAction()] caTextLayer.contentsScale = UIScreen.mainScreen().scale caTextLayer.frame = CGRectMake(playbackTimeImage.layer.bounds.origin.x, ((playbackTimeImage.layer.bounds.height - playbackTimeLayer.fontSize) / 2), playbackTimeImage.layer.bounds.width, playbackTimeLayer.fontSize * 1.2) uiImageTarget.layer.addSublayer(caTextLayer) caTextLayer.string = "The text you want to display" 

Ahora puedes actualizar caTextLayer.string todo lo que quieras =)

Inspirado por esto , y esta respuesta.

Prueba esto.

 let layer = CALayer() layer.delegate = hoo // Same lifecycle UIView instance. 

Advertencia

Si configuras el delegado de la instancia de UITableView, a veces ocurre un locking. (Probablemente, la prueba de último clic de scrollview se repite).

Si alguna vez necesitas una solución muy rápida (pero ciertamente hacky), valdría la pena hacerlo (Swift):

 let layer = CALayer() // set other properties // ... layer.speed = 999