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

2024-06-29

我不太明白 CALayer 是怎么做的display and drawInContext与......有关drawRect视图中。

如果我有一个 NSTimer 来设置[self.view setNeedsDisplay]每 1 秒,然后drawRect每 1 秒调用一次,如里面的 NSLog 语句所示drawRect.

但是如果我子类化 CALayer 并将其用于视图,如果我制作display方法为空,然后现在drawRect从未被调用过。Update: But display每 1 秒调用一次,如 NSLog 语句所示。

如果我删除那个空的display方法并添加一个空drawInContext方法,再次,drawRect从未被调用过。Update: But drawInContext每 1 秒调用一次,如 NSLog 语句所示。

究竟发生了什么?看起来display可以有选择地调用drawInContext and 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];
}

所以,假设,如果视图中的位置 (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 calls drawRect.

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

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


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

The -[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;
}

所以,如果你重写display,除非你打电话,否则这一切都不会发生[super display]。如果你实施displayLayer: in FooView,你必须创建自己的CGImage以某种方式并将其存储在图层中contents财产。

The -[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];
        }
    }
}

The onDraw据我所知,行动没有记录。

The -[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);
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

随机推荐