ios 在 -[CALayer setNeedsDisplayInRect:] 中禁用隐式动画
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2244147/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Disabling implicit animations in -[CALayer setNeedsDisplayInRect:]
提问by Ben Gottlieb
I've got a layer with some complex drawing code in its -drawInContext: method. I'm trying to minimize the amount of drawing I need to do, so I'm using -setNeedsDisplayInRect: to update just the changed parts. This is working splendidly. However, when the graphics system updates my layer, it's transitioning from the old to the new image using a cross-fade. I'd like it to switch over instantly.
我在它的 -drawInContext: 方法中有一个带有一些复杂绘图代码的图层。我试图尽量减少我需要做的绘图量,所以我使用 -setNeedsDisplayInRect: 来更新更改的部分。这工作得很好。但是,当图形系统更新我的图层时,它会使用交叉淡入淡出从旧图像过渡到新图像。我希望它立即切换。
I've tried using CATransaction to turn off actions and set the duration to zero, and neither work. Here's the code I'm using:
我尝试使用 CATransaction 关闭操作并将持续时间设置为零,但都不起作用。这是我正在使用的代码:
[CATransaction begin];
[CATransaction setDisableActions: YES];
[self setNeedsDisplayInRect: rect];
[CATransaction commit];
Is there a different method on CATransaction I should use instead (I also tried -setValue:forKey: with kCATransactionDisableActions, same result).
CATransaction 上是否有不同的方法我应该使用(我也尝试过 -setValue:forKey: 和 kCATransactionDisableActions,结果相同)。
回答by Brad Larson
You can do this by setting the actions dictionary on the layer to return [NSNull null]
as an animation for the appropriate key. For example, I use
您可以通过在图层上设置动作字典以[NSNull null]
作为适当键的动画返回来实现此目的。例如,我使用
NSDictionary *newActions = @{
@"onOrderIn": [NSNull null],
@"onOrderOut": [NSNull null],
@"sublayers": [NSNull null],
@"contents": [NSNull null],
@"bounds": [NSNull null]
};
layer.actions = newActions;
to disable fade in / out animations on insertion or change of sublayers within one of my layers, as well as changes in the size and contents of the layer. I believe the contents
key is the one you're looking for in order to prevent the crossfade on updated drawing.
在我的图层之一中插入或更改子图层时禁用淡入/淡出动画,以及图层大小和内容的更改。我认为,contents
关键是你以防止更新图纸的交叉淡入淡出寻找一个。
Swift version:
迅捷版:
let newActions = [
"onOrderIn": NSNull(),
"onOrderOut": NSNull(),
"sublayers": NSNull(),
"contents": NSNull(),
"bounds": NSNull(),
]
回答by mxcl
Also:
还:
[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
//foo
[CATransaction commit];
回答by user3378170
When you change the property of a layer, CA usually creates an implicit transaction object to animate the change. If you do not want to animate the change, you can disable implicit animations by creating an explicit transaction and setting its kCATransactionDisableActionsproperty to true.
当您更改图层的属性时,CA 通常会创建一个隐式事务对象来为更改设置动画。如果您不想为更改设置动画,可以通过创建显式事务并将其kCATransactionDisableActions属性设置为true来禁用隐式动画。
Objective-C
目标-C
[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
// change properties here without animation
[CATransaction commit];
Swift
迅速
CATransaction.begin()
CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions)
// change properties here without animation
CATransaction.commit()
回答by skozin
In addition to Brad Larson's answer: for custom layers (that are created by you) you can use delegationinstead of modifying layer's actions
dictionary. This approach is more dynamic and may be more performant. And it allows disabling all implicit animations without having to list all animatable keys.
除了Brad Larson 的回答:对于自定义图层(由您创建),您可以使用委托而不是修改图层的actions
字典。这种方法更具动态性并且可能更高效。它允许禁用所有隐式动画,而不必列出所有可设置动画的键。
Unfortunately, it's impossible to use UIView
s as custom layer delegates, because each UIView
is already a delegate of its own layer. But you can use a simple helper class like this:
不幸的是,不可能将UIView
s 用作自定义层委托,因为每个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
Usage (inside the view):
用法(在视图内):
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
Sometimes it's convenient to have view's controller as a delegate for view's custom sublayers; in this case there is no need for a helper class, you can implement actionForLayer:forKey:
method right inside the controller.
有时将视图的控制器作为视图的自定义子层的代理会很方便;在这种情况下,不需要辅助类,您可以actionForLayer:forKey:
在控制器内部实现方法。
Important note: don't try to modify the delegate of UIView
's underlying layer (e.g. to enable implicit animations)?— bad things will happen :)
重要提示:不要尝试修改UIView
底层的委托(例如启用隐式动画)? - 会发生不好的事情:)
Note: if you want to animate (not disable animation for) layer redraws, it is useless to put [CALayer setNeedsDisplayInRect:]
call inside a CATransaction
, because actual redrawing may (and probably will) happen sometimes later. The good approach is to use custom properties, as described in this answer.
注意:如果您想为图层重绘设置动画(而不是禁用动画),将[CALayer setNeedsDisplayInRect:]
call 放在 a 中是没有用的CATransaction
,因为实际重绘可能(并且可能会)稍后发生。好方法是使用自定义属性,如本答案中所述。
回答by Jarrod Smith
Here's a more efficient solution, similar to accepted answer but for Swift. For some cases it will be better than creating a transaction every time you modify the value which is a performance concern as others have mentioned e.g. common use-case of dragging the layer position around at 60fps.
这是一个更有效的解决方案,类似于已接受的答案,但适用于Swift。在某些情况下,这比每次修改值时都创建一个事务要好,这是一个性能问题,正如其他人提到的,例如以 60fps 拖动图层位置的常见用例。
// Disable implicit position animation.
layer.actions = ["position": NSNull()]
See apple's docs for how layer actions are resolved. Implementing the delegate would skip one more level in the cascade but in my case that was too messy due to the caveat about the delegate needing to be set to the associated UIView.
有关如何解决图层操作,请参阅 Apple 的文档。实现委托会在级联中再跳过一级,但在我的情况下,由于需要将委托设置为关联的 UIView的警告,这太混乱了。
Edit: Updated thanks to the commenter pointing out that NSNull
conforms to CAAction
.
编辑:由于评论者指出NSNull
符合CAAction
.
回答by Simon
Actually, I didn't find any of the answers to be the right one. The method that solves the problem for me was this:
实际上,我没有找到任何正确的答案。为我解决问题的方法是这样的:
- (id<CAAction>)actionForKey:(NSString *)event {
return nil;
}
Then you can whatever logic in it, to disable a specific animation, but since I wanted to removed them all, I returned nil.
然后你可以使用其中的任何逻辑来禁用特定的动画,但是由于我想将它们全部删除,所以我返回了 nil。
回答by bob
Based on Sam's answer, and Simon's difficulties... add the delegate reference after creating the CSShapeLayer:
基于 Sam 的回答和 Simon 的困难...在创建 CSSShapeLayer 后添加委托引用:
CAShapeLayer *myLayer = [CAShapeLayer layer];
myLayer.delegate = self; // <- set delegate here, it's magic.
... elsewhere in the "m" file...
...在“m”文件的其他地方...
Essentially the same as Sam's without the ability to toggle via the custom "disableImplicitAnimations" variable arrangement. More of a "hard-wire" approach.
基本上与 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];
}
回答by rounak
Found out a simpler method to disable action inside a CATransaction
that internally calls setValue:forKey:
for the kCATransactionDisableActions
key:
发现到关闭动作更简单的方法内部CATransaction
在内部调用setValue:forKey:
的kCATransactionDisableActions
关键:
[CATransaction setDisableActions:YES];
Swift:
迅速:
CATransaction.setDisableActions(true)
回答by pawpoise
To disable implicit layer animations in Swift
在 Swift 中禁用隐式图层动画
CATransaction.setDisableActions(true)
回答by Kamran Khan
Add this to your custom class where you are implementing -drawRect() method. Make changes to code to suite your needs, for me 'opacity' did the trick to stop cross-fade animation.
将此添加到您正在实现 -drawRect() 方法的自定义类中。对代码进行更改以满足您的需求,对我来说,“不透明度”可以阻止交叉淡入淡出动画。
-(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];
}