我的-drawInContext:方法中有一个包含一些复杂绘图代码的图层。我正在尝试最小化我需要做的绘图量,所以我使用-setNeedsDisplayInRect:仅更新已更改的部分。这是非常好的工作。但是,当图形系统更新我的图层时,它会使用交叉淡入淡出从旧图像过渡到新图像。我希望它能立即切换。
我尝试使用CATransaction来关闭操作并将持续时间设置为零,并且都不起作用。这是我正在使用的代码:
[CATransaction begin];
[CATransaction setDisableActions: YES];
[self setNeedsDisplayInRect: rect];
[CATransaction commit];
我应该使用不同的CATransaction方法(我也尝试过-setValue:forKey:使用kCATransactionDisableActions,结果相同)。
答案 0 :(得分:165)
您可以通过将图层上的操作字典设置为返回[NSNull null]
作为相应键的动画来完成此操作。例如,我使用
NSDictionary *newActions = @{
@"onOrderIn": [NSNull null],
@"onOrderOut": [NSNull null],
@"sublayers": [NSNull null],
@"contents": [NSNull null],
@"bounds": [NSNull null]
};
layer.actions = newActions;
在我的一个图层中插入或更改子图层时禁用淡入/淡出动画,以及图层大小和内容的更改。我相信contents
密钥是您正在寻找的密钥,以防止更新图纸上的交叉淡入淡出。
Swift版本:
let newActions = [
"onOrderIn": NSNull(),
"onOrderOut": NSNull(),
"sublayers": NSNull(),
"contents": NSNull(),
"bounds": NSNull(),
]
答案 1 :(得分:89)
此外:
[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
//foo
[CATransaction commit];
答案 2 :(得分:29)
当您更改图层的属性时,CA通常会创建一个隐式事务对象来为更改设置动画。如果您不想为更改设置动画,可以通过创建显式事务并将其 kCATransactionDisableActions 属性设置为 true 来禁用隐式动画。
目标-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()
答案 3 :(得分:23)
除Brad Larson's answer之外:对于自定义图层(由您创建)you can use delegation而不是修改图层的actions
字典。这种方法更具动态性,可能更具性能。它允许禁用所有隐式动画,而无需列出所有可设置动画的键。
不幸的是,不可能将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
底层图层的代理(例如启用隐式动画) - 会发生不好的事情:)
注意:如果你想动画(不禁用动画)图层重绘,将[CALayer setNeedsDisplayInRect:]
调用放在CATransaction
内是没用的,因为实际的重绘可能(有时可能会)稍后发生。好方法是使用自定义属性,如in this answer所述。
答案 4 :(得分:8)
这是一个更有效的解决方案,类似于接受的答案,但 Swift 。对于某些情况,每次修改一个性能问题的值时创建一个事务会比其他人提到的更好,例如常见的用例 - 以60fps左右拖动图层位置。
// Disable implicit position animation.
layer.actions = ["position": NSNull()]
请参阅Apple的how layer actions are resolved文档。实现委托会在级联中跳过一个级别,但在我的情况下,由于caveat about the delegate needing to be set to the associated UIView而过于混乱。
修改:感谢评论者指出NSNull
符合CAAction
。
答案 5 :(得分:7)
实际上,我没有找到任何合适的答案。解决这个问题的方法是:
- (id<CAAction>)actionForKey:(NSString *)event {
return nil;
}
然后你可以使用其中的任何逻辑来禁用特定的动画,但由于我想将它们全部删除,所以我返回了nil。
答案 6 :(得分:7)
根据Sam的回答和Simon的困难...在创建CSShapeLayer后添加委托引用:
CAShapeLayer *myLayer = [CAShapeLayer layer];
myLayer.delegate = self; // <- set delegate here, it's magic.
......“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];
}
答案 7 :(得分:5)
找出一种更简单的方法来禁用CATransaction
内部为setValue:forKey:
键内部调用kCATransactionDisableActions
的操作:
[CATransaction setDisableActions:YES];
夫特:
CATransaction.setDisableActions(true)
答案 8 :(得分:3)
在Swift中禁用隐式图层动画
CATransaction.setDisableActions(true)
答案 9 :(得分:2)
将此添加到您正在实现-drawRect()方法的自定义类中。根据您的需求更改代码,对我来说,不透明度&#39;做了阻止交叉渐变动画的技巧。
-(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];
}
答案 10 :(得分:0)
从 iOS 7 开始,有一种方便的方法可以做到这一点:
[UIView performWithoutAnimation:^{
// apply changes
}];
答案 11 :(得分:0)
要在更改CATextLayer的字符串属性时禁用恼人(模糊)动画,您可以执行以下操作:
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 =)
答案 12 :(得分:0)
试试这个。
let layer = CALayer()
layer.delegate = hoo // Same lifecycle UIView instance.
警告强>
如果设置UITableView实例的委托,有时会发生崩溃。(可能是递归调用scrollview的hittest。)
答案 13 :(得分:0)
如果你需要一个非常快速(但不可否认的hacky)修复,那么它可能值得做(Swift):
let layer = CALayer()
// set other properties
// ...
layer.speed = 999
答案 14 :(得分:0)
已更新,可在iOS而不是MacOS中快速且仅禁用一个隐式属性动画
// 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)
}