动画CALayer的界限w /重绘

时间:2011-09-27 23:43:34

标签: iphone objective-c core-animation calayer bounds

我想知道如何为CALayer的边界设置动画,因此,在每个边界更改时,图层会调用drawInContext:。我在我的CALayer子类上尝试了以下两种方法:

  • needsDisplayOnBoundsChange设为YES
  • YES密钥<+ (BOOL)needsDisplayForKey:(NSString*)key返回bounds

都没有工作。 CALayer似乎决定使用图层的原始内容,只需根据contentsGravity进行缩放(我认为这是为了表现。)这是他们的解决方法还是我错过了一些明显的东西?

编辑:而且,顺便说一下,我注意到我的自定义CALayer子类没有调用initWithLayer:来创建presentationLayer - 很奇怪。

提前致谢, 萨姆

5 个答案:

答案 0 :(得分:7)

您可以使用technique outlined here:覆盖CALayer的+needsDisplayForKey:方法,它会在动画的每一步重绘其内容。

答案 1 :(得分:1)

我不确定将viewFlags设置为有效。第二种解决方案肯定不会起作用:

  

默认实现返回NO。子类应该*调用super   对于超类定义的属性。 (例如,*不要尝试   为CALayer实现的属性返回YES,*做意愿   有不明确的结果。)

您需要将视图的内容模式设置为UIViewContentModeRedraw:

UIViewContentModeRedraw,    //redraw on bounds change (calls -setNeedsDisplay)

结帐Apple's documentation on providing content with CALayer's。他们建议使用CALayer的委托属性而不是子类,这可能比你现在尝试的要容易得多。

答案 2 :(得分:0)

我不知道这是否完全有资格作为您问题的解决方案,但能够让它发挥作用。

我首先将我的内容图片预先绘制成CGImageRef

然后我覆盖了我的图层 INSTEAD OF -display的{​​{1}}方法。在其中我将-drawInContext:设置为预渲染的CGImage,并且它有效。

最后,您的图层还需要将默认contents更改为contentsGravity,以避免缩放内容图像。

我遇到的问题是传递给@"left"的上下文是图层的起始大小,而不是最终的动画后大小。 (您可以使用-drawInContext:CGBitmapContextGetWidth方法进行检查。)

我的方法仍然只为整个动画调用一次,但使用CGBitmapContextGetHeight方法直接设置图层的内容允许您传递大于可见边界的图像。 -display方法不允许这样做,因为您无法在CGContext上下文的范围之外绘制。

有关不同图层绘制方法之间差异的更多信息,请参阅http://www.apeth.com/iOSBook/ch16.html

答案 3 :(得分:0)

我最近遇到了同样的问题。这就是我发现的:

你可以做到这一点,即动画像边界的阴影副本:

var shadowBounds: CGRect {
  get { return bounds }
  set { bounds = newValue}
}

然后覆盖CALayer的+ needsDisplayForKey:。

但是,如果您的绘图取决于边界,这可能不是您想要做的。正如您已经注意到的,核心动画只是将图层的内容缩放为动画边界。即使您执行上述技巧也是如此,即,即使在动画期间更改了边界,内容也会缩放。结果是你的绘画动画看起来不一致。

如何解决?由于内容是缩放的,您可以通过反向缩放来计算确定绘图的自定义变量的值,以便您在最终但缩放的内容上绘制的内容与原始和未缩放的内容相同,然后将fromValues设置为这些值, toValues到他们的旧值,用边界同时为它们制作动画。如果要更改最终值,请将toValues设置为这些最终值。您必须为至少一个自定义变量设置动画,以便进行重绘。

答案 4 :(得分:-2)

这是您的自定义类:

@implementation MyLayer

-(id)init
{
    self = [super init];
    if (self != nil)
        self.actions = [NSDictionary dictionaryWithObjectsAndKeys:
                        [NSNull null], @"bounds",
                        nil];
    return self;
}

-(void)drawInContext:(CGContextRef)context
{
    CGContextSetRGBFillColor(context,
                             drand48(),
                             drand48(),
                             drand48(),
                             1);
    CGContextFillRect(context,
                      CGContextGetClipBoundingBox(context));
}

+(BOOL)needsDisplayForKey:(NSString*)key
{
    if ([key isEqualToString:@"bounds"])
        return YES;
    return [super needsDisplayForKey:key];
}

@end

这些是对xcode 4.2默认模板的补充:

-(BOOL)application:(UIApplication*)application
didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
        // Override point for customization after application launch.
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];

        // create and add layer
    MyLayer *layer = [MyLayer layer];
    [self.window.layer addSublayer:layer];
    [self performSelector:@selector(changeBounds:)
               withObject:layer];

    return YES;
}

-(void)changeBounds:(MyLayer*)layer
{
        // change bounds
    layer.bounds = CGRectMake(0, 0,
                              drand48() * CGRectGetWidth(self.window.bounds),
                              drand48() * CGRectGetHeight(self.window.bounds));

        // call "when idle"
    [self performSelector:@selector(changeBounds:)
               withObject:layer
               afterDelay:0];
}

-----------------编辑:

好的......这不是你要求的:)对不起:|

-----------------编辑(2):

为什么你需要这样的东西?可以使用(void)display,但文档说它可用于设置self.contents ...