在 – 中禁用隐式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