在 - [CALayer setNeedsDisplayInRect:]中禁用隐式动画

Ben*_*ieb 133 iphone core-animation calayer ios

我的-drawInContext:方法中有一个包含一些复杂绘图代码的图层.我正在尝试最小化我需要做的绘图量,所以我使用-setNeedsDisplayInRect:仅更新已更改的部分.这是非常好的工作.但是,当图形系统更新我的图层时,它会使用交叉淡入淡出从旧图像过渡到新图像.我希望它能立即切换.

我已经尝试使用CATransaction来关闭操作并将持续时间设置为零,并且都不起作用.这是我正在使用的代码:

[CATransaction begin];
[CATransaction setDisableActions: YES];
[self setNeedsDisplayInRect: rect];
[CATransaction commit];
Run Code Online (Sandbox Code Playgroud)

我应该使用不同的CATransaction方法(我也尝试过-setValue:forKey:使用kCATransactionDisableActions,结果相同).

Bra*_*son 168

您可以通过将图层上的操作字典设置[NSNull null]为作为相应键的动画返回来执行此操作.例如,我用

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

layer.actions = newActions;
Run Code Online (Sandbox Code Playgroud)

在我的一个图层中插入或更改子图层时禁用淡入/淡出动画,以及图层大小和内容的更改.我相信contents关键是你正在寻找的关键是为了防止更新图纸上的交叉淡入淡出.


Swift版本:

let newActions = [
        "onOrderIn": NSNull(),
        "onOrderOut": NSNull(),
        "sublayers": NSNull(),
        "contents": NSNull(),
        "bounds": NSNull(),
    ]
Run Code Online (Sandbox Code Playgroud)

  • 要在更换框架时防止移动,请使用"@"位置""键. (24认同)
  • 如果要切换图层的可见性并希望禁用不透明度动画,也一定要在动作字典中添加"@"隐藏的"属性". (11认同)
  • 这些"特殊"字符串实际上有一些不代表属性的常量(例如@"onOrderOut"的kCAOnOrderOut),这里有详细记录:https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreAnimation_guide /Articles/Actions.html#//apple_ref/doc/uid/TP40006095-SW1 (11认同)
  • @Benjohn只有没有相应属性的键才定义了常量。顺便说一句,链接似乎已死,这是新的网址:https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CoreAnimation_guide/ReactingtoLayerChanges/ReactingtoLayerChanges.html#//apple_ref/doc/uid/ TP40004514-CH7-SW1 (2认同)

mxc*_*xcl 90

也:

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

//foo

[CATransaction commit];
Run Code Online (Sandbox Code Playgroud)

  • 简写:`[CATransaction setDisableActions:YES]` (26认同)
  • 添加@titaniumdecoy注释,以防万一有人感到困惑(像我一样),`[CATransaction setDisableActions:YES]`只是`[CATransaction setValue:forKey:]`行的简写.你仍然需要`begin`和`commit`行. (7认同)
  • 你可以用`[self setNeedsDisplayInRect:rect]替换`// foo`; [self displayIfNeeded];`回答原来的问题. (3认同)
  • 导致性能问题,设置操作更高效 (3认同)

use*_*170 31

更改图层的属性时,CA通常会创建一个隐式事务对象来为更改设置动画.如果您不想为更改设置动画,可以通过创建显式事务并将其kCATransactionDisableActions属性设置为true来禁用隐式动画.

Objective-C的

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

迅速

CATransaction.begin()
CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions)
// change properties here without animation
CATransaction.commit()
Run Code Online (Sandbox Code Playgroud)

  • setDisableActions:做同样的事情. (6认同)
  • 这是我在Swift工作的最简单的解决方案! (3认同)

sko*_*zin 23

除了Brad Larson的回答:对于自定义图层(由您创建),您可以使用委托而不是修改图层的actions字典.这种方法更具动态性,可能更具性能.它允许禁用所有隐式动画,而无需列出所有可设置动画的键.

不幸的是,不可能将UIViews用作自定义层委托,因为每个UIView委托都已经是自己层的委托.但你可以使用这样一个简单的助手类:

@interface MyLayerDelegate : NSObject
    @property (nonatomic, assign) BOOL disableImplicitAnimations;
@end

@implementation MyLayerDelegate

- (id<CAAction>)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
Run Code Online (Sandbox Code Playgroud)

用法(在视图内):

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
Run Code Online (Sandbox Code Playgroud)

有时将视图的控制器作为视图的自定义子层的委托是很方便的; 在这种情况下,不需要辅助类,您可以actionForLayer:forKey:在控制器内部实现方法.

重要提示:不要尝试修改UIView底层的委托(例如启用隐式动画) - 会发生不好的事情:)

注意:如果你想要动画(不要禁用动画)图层重绘,将[CALayer setNeedsDisplayInRect:]调用放在一个内部是没用的CATransaction,因为实际的重绘可能(有时可能会)稍后发生.好的方法是使用自定义属性,如本答案中所述.


Jar*_*ith 9

这是一个更有效的解决方案,类似于接受的答案,但对于Swift.对于某些情况,每次修改一个性能问题的值时,它会比创建一个事务更好,正如其他人提到的那样,例如以60fps左右拖动图层位置的常见用例.

// Disable implicit position animation.
layer.actions = ["position": NSNull()]      
Run Code Online (Sandbox Code Playgroud)

有关如何解析图层操作的信息,请参阅Apple的文档.实现委托会跳过级联中的另一个级别,但在我的情况下由于需要设置为关联的UIView的委托警告而太乱.

编辑:更新了感谢指出NSNull符合的评论者CAAction.


Sim*_*mon 7

实际上,我没有找到任何合适的答案.解决这个问题的方法是这样的:

- (id<CAAction>)actionForKey:(NSString *)event {   
    return nil;   
}
Run Code Online (Sandbox Code Playgroud)

然后你可以使用它中的任何逻辑来禁用特定的动画,但是因为我想要将它们全部删除,所以我返回了nil.


bob*_*bob 7

根据Sam的回答和Simon的困难...在创建CSShapeLayer后添加委托引用:

CAShapeLayer *myLayer = [CAShapeLayer layer];
myLayer.delegate = self; // <- set delegate here, it's magic.
Run Code Online (Sandbox Code Playgroud)

......"m"文件中的其他地方......

基本上与Sam没有通过自定义"disableImplicitAnimations"变量排列切换的能力相同.更多的是"硬线"方法.

- (id<CAAction>)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];

}
Run Code Online (Sandbox Code Playgroud)


小智 6

在 Swift 中禁用隐式图层动画

CATransaction.setDisableActions(true)
Run Code Online (Sandbox Code Playgroud)


rou*_*nak 5

找到了一个更简单的方法来禁用内动作CATransaction在内部调用setValue:forKey:kCATransactionDisableActions关键:

[CATransaction setDisableActions:YES];
Run Code Online (Sandbox Code Playgroud)

迅速:

CATransaction.setDisableActions(true)
Run Code Online (Sandbox Code Playgroud)