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



Answers

也:

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

//foo

[CATransaction commit];
Question

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

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

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

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




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

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



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

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

然后你可以使用任何逻辑来禁用特定的动画,但是因为我想把它们全部删除,所以我返回零。




除了Brad Larson的回答 :对于自定义图层(由您创建), 您可以使用委派而不是修改图层的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底层的委托(例如启用隐式动画) - 坏事会发生:)

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




将此添加到您正在实现-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];
}



尝试这个。

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

警告

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




在Swift中禁用隐式层动画

CATransaction.setDisableActions(true)



Links