在iOS上,setNeedsDisplay确实不会导致调用drawRect ...除非CALayer的display或drawInContext最终调用drawRect?

Jer*_*y L 3 core-animation calayer uiview drawrect ios

我真的不知道如何的CALayer的display,并drawInContext涉及到drawRect在视图中.

如果我有一个NSTimer设置[self.view setNeedsDisplay]每1秒,则drawRect每1秒调用一次,如内部的NSLog语句所示drawRect.

但是如果我将CALayer子类化并将其用于视图,如果我将该display方法设为空,那么现在drawRect永远不会被调用. 更新:display每隔1秒调用一次,如NSLog语句所示.

如果我删除该空display方法并添加一个空drawInContext方法,drawRect则永远不会被调用. 更新:drawInContext每隔1秒调用一次,如NSLog语句所示.

究竟发生了什么?似乎display可以选择性地调用drawInContext并且drawInContext可以选择性地以某种方式调用drawRect(如何?),但这里的真实情况是什么?


更新:答案有更多线索:

我将CoolLayer.m代码更改为以下内容:

-(void) display {
    NSLog(@"In CoolLayer's display method");
    [super display];
}

-(void) drawInContext:(CGContextRef)ctx {
    NSLog(@"In CoolLayer's drawInContext method");
    [super drawInContext:ctx];
}
Run Code Online (Sandbox Code Playgroud)

所以,比方说,如果在视图中的位置(100,100)有月亮(由Core Graphics绘制的圆圈),现在我将其更改为位置(200,200),当然,我会打电话[self.view setNeedsDisplay],现在,CALayer将会新视图图像根本没有缓存,因为我drawRect指示现在应该如何显示月亮.

即便如此,入口点是CALayer display,然后是CALayer drawInContext:如果我设置了一个断点drawRect,则调用堆栈显示:

在此输入图像描述

所以我们可以看到CoolLayer display首先进入,然后是CALayer display,然后是CoolLayer drawInContext,然后是CALayer drawInContext,即使在这种情况下,新图像也没有这样的缓存.

最后,CALayer drawInContext呼叫代表drawLayer:InContext.委托是视图(FooView或UIView)...并且drawLayer:InContext是UIView 中的默认实现(因为我没有覆盖它).最后是drawLayer:InContext电话drawRect.

所以我猜两点:即使没有图像缓存,为什么它会进入CALayer?因为通过这种机制,图像在上下文中绘制,最后返回display,并且CGImage是从这个上下文创建的,然后它现在被设置为缓存的新图像.这就是CALayer缓存图像的方式.

我不太确定的另一件事是:如果[self.view setNeedsDisplay]总是触发drawRect被调用,那么何时可以使用CALayer中的缓存图像?可能是......在Mac OS X上,当另一个窗口遮住窗口时,现在顶部的窗口被移开了.现在我们不需要调用drawRect重绘所有内容,但可以使用CALayer中的缓存图像.或者在iOS上,如果我们停止应用程序,执行其他操作,然后返回应用程序,则可以使用缓存的图像,而不是调用drawRect.但如何区分这两种"脏"?一个是"未知的脏" - 月亮需要按drawRect逻辑指示重绘(它也可以在那里使用随机数作为坐标).其他类型的脏是它被掩盖或消失,现在需要重新显示.

rob*_*off 12

当需要显示某个层并且没有有效的后备存储(可能是因为该层接收到setNeedsDisplay消息)时,系统会将该display消息发送到该层.

-[CALayer display]方法看起来大致如下:

- (void)display {
    if ([self.delegate respondsToSelector:@selector(displayLayer:)]) {
        [[self.delegate retain] displayLayer:self];
        [self.delegate release];
        return;
    }

    CABackingStoreRef backing = _backingStore;
    if (!backing) {
        backing = _backingStore = ... code here to create and configure
            the CABackingStore properly, given the layer size, isOpaque,
            contentScale, etc.
    }

    CGContextRef gc = ... code here to create a CGContext that draws into backing,
        with the proper clip region
    ... also code to set up a bitmap in memory shared with the WindowServer process

    [self drawInContext:gc];
    self.contents = backing;
}
Run Code Online (Sandbox Code Playgroud)

所以,如果你覆盖display,除非你打电话,否则不会发生这种情况[super display].如果你执行displayLayer:FooView,你必须创建自己CGImage莫名其妙并将其存储在图层的contents属性.

-[CALayer drawInContext:]方法看起来大致如下:

- (void)drawInContext:(CGContextRef)gc {
    if ([self.delegate respondsToSelector:@selector(drawLayer:inContext:)]) {
        [[self.delegate retain] drawLayer:self inContext:gc];
        [self.delegate release];
        return;
    } else {
        CAAction *action = [self actionForKey:@"onDraw"];
        if (action) {
            NSDictionary *args = [NSDictionary dictionaryWithObject:gc forKey:@"context"];
            [action runActionForKey:@"onDraw" object:self arguments:args];
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

onDraw据我所知,该行动未记录在案.

-[UIView drawLayer:inContext:]方法看起来大致如下:

- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)gc {
    set gc's stroke and fill color spaces to device RGB;
    UIGraphicsPushContext(gc);
    fill gc with the view's background color;
    if ([self respondsToSelector:@selector(drawRect:)]) {
        [self drawRect:CGContextGetClipBoundingBox(gc)];
    }
    UIGraphicsPopContext(gc);
}
Run Code Online (Sandbox Code Playgroud)

  • 我使用Hopper来反汇编IOS模拟器库. (4认同)