在iOS上,setNeedsDisplay确实不会导致调用drawRect ...除非CALayer的display或drawInContext最终调用drawRect? [英] On iOS, setNeedsDisplay really doesn't cause drawRect to be called... unless CALayer's display or drawInContext finally calls drawRect?

查看:122
本文介绍了在iOS上,setNeedsDisplay确实不会导致调用drawRect ...除非CALayer的display或drawInContext最终调用drawRect?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我不太了解CALayer的显示 drawInContext drawRect 视图中。

I don't really understand how CALayer's display and drawInContext relate to drawRect in the view.

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

If I have an NSTimer that sets the [self.view setNeedsDisplay] every 1 second, then drawRect is called every 1 second, as shown by an NSLog statement inside of drawRect.

但是,如果我将CALayer子类化并用于视图,则如果将 display 方法设为空,则现在 drawRect 从未被调用。 更新:但是,每1秒调用一次 display ,如NSLog语句所示。

But if I subclass a CALayer and use that for the view, if I make the display method empty, then now drawRect is never called. Update: But display is called every 1 second, as shown by an NSLog statement.

如果我删除了空的 display 方法并添加了空的 drawInContext 方法,则再次是 drawRect 从未被调用。 更新:但是,每1秒调用一次$code> drawInContext ,如NSLog语句所示。

If I remove that empty display method and add an empty drawInContext method, again, drawRect is never called. Update: But drawInContext is called every 1 second, as shown by an NSLog statement.

到底发生了什么事?似乎 display 可以有选择地调用 drawInContext ,而 drawInContext 可以有选择地以某种方式调用 drawRect (如何?),但是这里的实际情况是什么?

What is exactly happening? It seems that display can selectively call drawInContext and drawInContext can selectively somehow call drawRect (how?), but what is the real situation here?

更新:答案的更多线索:

我更改了 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),现在我将其更改为位置(200,200),自然地,我将调用 [self.view setNeedsDisplay] ,由于我的 drawRect 规定了现在应该如何显示月亮,因此CALayer根本不会为新视图图像缓存。

So, let's say, if there is a moon (as a circle drawn by Core Graphics) at location (100,100) in the View, and now I change it to location (200,200), naturally, I will call [self.view setNeedsDisplay], and now, CALayer will have no cache at all for the new view image, as my drawRect dictates how the moon should now be displayed.

即使如此,入口点还是CALayer的 display ,然后是CALayer的 drawInContext :如果我设置了一个中断指向 drawRect ,调用堆栈显示:

Even so, the entry point is CALayer's display, and then CALayer's drawInContext: If I set a break point at drawRect, the call stack shows:

因此我们可以看到CoolLayer的显示,然后转到CALayer的显示,然后进入CoolLayer的 drawInContext ,然后进入CALayer的 drawInContext ,即使在这种情况下,新图像也不存在这样的缓存。

So we can see that CoolLayer's display is entered first, and it goes to CALayer's display, and then CoolLayer's drawInContext, and then CALayer's drawInContext, even though in this situation, no such cache exist for the new image.

最后,CALayer的 drawInContext 调用了委托人的 drawLayer:InContext 。委托是视图(FooView或UIView)...,而 drawLayer:InContext 是UIView中的默认实现(因为我没有覆盖它)。最后是 drawLayer:InContext 调用 drawRect

Then finally, CALayer's drawInContext calls the delegate's drawLayer:InContext. The delegate is the view (FooView or UIView)... and drawLayer:InContext is the default implementation in UIView (as I did not override it). It is finally that drawLayer:InContext calls drawRect.

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

So I am guessing two points: why does it enter CALayer even though there is no cache for the image? Because through this mechanism, the image is drawn in the context, and finally returns to display, and the CGImage is created from this context, and then it is now set as the new image cached. This is how CALayer caches images.

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

Another thing I am not quite sure is: if [self.view setNeedsDisplay] always trigger drawRect to be called, then when can a cached image in CALayer be used? Could it be... on Mac OS X, when another window covers up a window, and now the top window is moved away. Now we don't need to call drawRect to redraw everything, but can use the cached image in the CALayer. Or on iOS, if we stop the app, do something else, and come back to the app, then the cached image can be used, instead of calling drawRect. But how to distinguish these two types of "dirty"? One is a "unknown dirty" -- that the moon needs to be redrawn as dictated by the drawRect logic (it can use a random number there for the coordinate too). The other types of dirty is that it was covered up or made to disappear, and now needs to be re-shown.

推荐答案

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

When a layer needs to be displayed and has no valid backing store (perhaps because the layer received a setNeedsDisplay message), the system sends the display message to the layer.

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

因此,如果您覆盖显示,除非您调用 [超级显示] ,否则这些都不会发生。如果您在 FooView 中实现 displayLayer:,则必须创建自己的 CGImage 以某种方式将其存储在图层的 contents 属性中。

So, if you override display, none of that happens unless you call [super display]. And if you implement displayLayer: in FooView, you have to create your own CGImage somehow and store it in the layer's contents property.

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

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);
}

这篇关于在iOS上,setNeedsDisplay确实不会导致调用drawRect ...除非CALayer的display或drawInContext最终调用drawRect?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆