2011-09-27 38 views
16

我想知道如何动画CALayer的界限,所以,在每个界限的变化,层调用drawInContext:。我已经试过2种以下方法,对我的CALayer的子类:动画CALayer的界限瓦特/重绘

  • 设置needsDisplayOnBoundsChangeYES
  • bounds关键

无论工作返回YES为+ (BOOL)needsDisplayForKey:(NSString*)key。 CALayer似乎决定使用该图层的原始内容,并根据contentsGravity(我认为这是为了提高性能)对它们进行缩放。他们是这样做的解决方法还是我错过了明显的东西?

编辑:而且,顺便说一句,我发现我的自定义CALayer子不叫initWithLayer:创建presentationLayer - 怪异。

由于提前, 山姆

+0

另一件行不通的事:子类化和重载'setFrame','setBounds'和'setPosition'。在动画过程中不会调用它们。 –

+0

我不太了解你。你想要做什么动画?只是CALayer的界限或别的什么?限制动画是非常简单的任务,框架动画 - 更复杂。 – beryllium

+0

想象一下,您的图层包含类似具有复杂但大小独立的边框图形的按钮。如果将动画设置为宽度的两倍,则它将使用位图缩放进行动画制作,在整个动画中变得拉伸并像素化,即使您拥有“needsDisplayOnBoundsChange”YES。只有最后一帧才能用'drawInContext:'正确渲染。 –

回答

1

我不知道该设置viewFlags将是有效的。第二种解决方案绝对不行:

默认实现返回NO。对于由超类定义的属性,子类应该*调用超级 。 (例如,*不要试图 返回YES通过的CALayer实现的属性,*做法将 无法预料的结果。)

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

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

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

+0

UIViewContentModeRedraw不会在动画的每个步骤中重绘图层的内容。 – titaniumdecoy

+0

真的吗?嗯......你知道在动画过程中是否调用CALayerDelegate方法吗? –

+0

除非你使用我在我的答案中链接的技巧(这是覆盖CALayer的'+ needsDisplayForKey:'方法) – titaniumdecoy

6

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

+0

这是一个很好的答案。在我们点击之前提供一些关于链接的更多信息。 –

+0

尽管奇怪的是,CALayer不会显示这些重绘的结果,但会继续提前生成的拉伸内容,这种方法确实会为每帧都调用重绘方法。不过,这是一个重大的进步。 –

+0

@AlexanderLjungberg:这种方法应该在动画的每一步都重新绘制内容。我创建了一个测试项目来确认它的工作原理。我不确定可能会导致图层无法重​​新显示。 – titaniumdecoy

-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 ...

+0

这不起作用:+ needsDisplayForKey不强制重绘“边界” –

0

我不知道这是否完全符合您的问题的解决方案,但能够得到这个工作。

我首先将我的内容图像预先画成CGImageRef

然后我覆盖了我的图层的-display方法INSTEAD的-drawInContext:。在其中,我将contents设置为预渲染的CGImage,并且工作正常。

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

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

我的方法仍然只能调用一次,整个动画,而是直接与-display方法设置图层的内容可以让你通过图片比可视范围更大。 drawInContext:方法不允许这样做,因为你不能在CGContext上下文的范围之外绘制。

欲了解更多有关不同图层绘制方法之间的差异,看到http://www.apeth.com/iOSBook/ch16.html

0

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

有一个小窍门,你可以做到这一点,那就是动画界的卷影副本,如:

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

然后覆盖的CALayer的+ needsDisplayForKey :.

但是,这可能不是你想要做的,如果你的绘图依赖于边界。正如您已经注意到的,核心动画只是简单地将图层的内容缩放为动画边界。即使您执行了上述技巧,情况也是如此,即,即使动画期间边界发生变化,内容也会缩放。结果是你的图画动画看起来不一致。

如何解决?由于内容是缩放的,因此可以通过反转缩放来计算自定义变量的值,以确定最终但缩放后的内容的绘制看起来与原始缩放内容相同,然后将fromValues设置为这些值, toValues到他们以前的价值观,在界限的同时为他们制作动画。如果要更改最终值,请将toValues设置为这些最终值。您必须动画至少一个自定义变量以导致重绘。