在 – 中禁用隐式animation

我在它的-drawInContext:方法中有一个复杂的绘图代码层。 我想尽量减less我需要做的绘图的数量,所以我使用-setNeedsDisplayInRect:只更新更改的部分。 这是出色的。 但是,当graphics系统更新我的图层时,它将使用交叉渐变从旧图像过渡到新图像。 我想立即切换。

我已经尝试使用CATransaction来closures操作并将持续时间设置为零,但都不起作用。 以下是我正在使用的代码:

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

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

您可以通过设置图层上的动作字典将[NSNull null]作为适当的键的animation来完成。 例如,我使用

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

在插入或更改某个图层内的子图层时禁用淡入/淡出animation,以及图层大小和内容的更改。 我相信contents关键是你正在寻找的,以防止交叉淡入淡出的图纸。

也:

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

除了Brad Larson的回答 :对于自定义图层(由您创build), 您可以使用委派而不是修改图层的actions字典。 这种方法更具有dynamic性,可能更具性能。 它允许禁用所有隐式animation,而不必列出所有的animation键。

不幸的是,使用UIView作为自定义图层委托是不可能的,因为每个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 

用法(在视图内):

 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 

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

重要提示:不要尝试修改UIView底层的委托(例如启用隐式animation) – 坏事会发生:)

注意:如果你想animation(而不是禁用animation)图层重绘,那么在CATransaction放置[CALayer setNeedsDisplayInRect:]调用是没有用的,因为实际的重绘可能(也可能会)稍后发生。 好的方法是使用自定义属性, 如此答案中所述 。

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

Objective-C的

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

迅速

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

基于山姆的回答,和西蒙的困难…创buildCSShapeLayer后添加委托引用:

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

在“m”文件中的其他地方…

基本上与Sam's一样,没有通过自定义的“disableImplicitAnimations”variables排列来切换的能力。 更多的是“硬线”的方法。

 - (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]; } 

其实,我没有find任何答案是正确的。 解决这个问题的方法是这样的:

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

那么你可以使用任何逻辑来禁用一个特定的animation,但是因为我想把它们全部删除,所以我返回了零。

这是一个更有效的解决scheme,类似于接受的答案,但Swift 。 对于某些情况来说,每次修改性能问题时都会创build一个事务,比如其他人提到的性能问题,例如常见的使用情况 – 以60fps的速度拖动图层位置。

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

请参阅苹果的文档了解如何解决层操作 。 实现委托会跳过层叠中的一个级别,但在我的情况下,由于关于委托需要设置为关联的UIView的警告太麻烦。

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

将此添加到您正在实现-drawRect()方法的自定义类中。 对代码进行更改以满足您的需求,对于我来说,“不透明”技术阻止了淡入淡出animation。

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

find一个简单的方法来禁用内部调用setValue:forKey:CATransaction内部的操作setValue:forKey:对于kCATransactionDisableActions项:

 [CATransaction setDisableActions:YES]; 

iOS 7开始,有一个方便的方法可以做到这一点:

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

要在更改CATextLayer的string属性时禁用恼人的(模糊)animation,可以这样做:

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

然后像这样使用它(不要忘记正确设置您的CATextLayer,例如正确的字体等):

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

您可以在这里看到我的CATextLayer的完整设置:

 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" 

现在你可以更新caTextLayer.string,只要你想=)

受这个启发, 这个答案。

尝试这个。

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

警告

如果你设置了UITableView实例的委托,有时会发生崩溃(可能是recursion调用scrollview的命中)。

如果你需要一个非常快速(但毫无疑问是hacky)的修复,那么值得做一下(Swift):

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