17

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

  • 设置needsDisplayOnBoundsChangeYES
  • 返回键的YES+(BOOL)needsDisplayForKey:(NSString*)keybounds

都不工作。CALayer 似乎决定使用图层的原始内容并简单地根据它们进行缩放contentsGravity(我认为这是为了性能。)它们是解决这个问题的方法还是我遗漏了一些明显的东西?

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

在此先感谢,山姆

4

5 回答 5

7

您可以使用此处概述的技术:覆盖 CALayer 的+needsDisplayForKey:方法,它将在动画的每一步重绘其内容。

于 2011-10-31T02:51:17.473 回答
1

我不确定设置 viewFlags 是否有效。第二种解决方案肯定行不通:

默认实现返回 NO。子类应该 * 为超类定义的属性调用 super。(例如,* 不要尝试为 CALayer 实现的属性返回 YES,* 这样做会产生未定义的结果。)

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

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

查看Apple 关于使用 CALayer 提供内容的文档。他们建议使用 CALayer 的委托属性而不是子类,这可能比您现在尝试的要容易得多。

于 2011-10-31T01:52:48.103 回答
0

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

有一个技巧可以做到,即为边界的影子副本设置动画,例如:

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

然后覆盖 CALayer 的 +needsDisplayForKey:。

但是,如果您的绘图取决于边界,这可能不是您想要做的。正如您已经注意到的那样,核心动画只是将图层的内容缩放到动画边界。即使您使用上述技巧也是如此,也就是说,即使在动画期间边界发生变化,内容也会被缩放。结果是您的绘图动画看起来不一致。

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

于 2017-07-16T04:58:09.120 回答
0

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

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

然后我覆盖了-display我的图层INSTEAD OF -drawInContext:的方法。在其中我设置contents了预渲染的 CGImage,它工作了。

最后,您的图层还需要将默认值更改contentsGravity为类似@"left"的内容,以避免按比例绘制内容图像。

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

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

更多关于不同图层绘制方式的区别,见http://www.aeth.com/iOSBook/ch16.html

于 2015-11-05T04:56:04.040 回答
-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...

于 2011-11-01T22:59:16.603 回答