在iOS上,setNeedsDisplay实际上并不';t导致调用drawRect。。。除非CALayer';s display或drawInContext最终调用drawRect?

在iOS上,setNeedsDisplay实际上并不';t导致调用drawRect。。。除非CALayer';s display或drawInContext最终调用drawRect?,ios,uiview,core-animation,calayer,drawrect,Ios,Uiview,Core Animation,Calayer,Drawrect,我真的不明白CALayer的显示和DrawInText与视图中的drawRect有什么关系 如果我有一个NSTimer,它每1秒设置一次[self.view setNeedsDisplay],则每隔1秒调用一次drawRect,如drawRect中的NSLog语句所示 但是如果我将CALayer子类化并将其用于视图,如果我将display方法设为空,那么现在就永远不会调用drawRectUpdate:但每隔1秒调用一次display,如NSLog语句所示 如果删除空的display方法并添加空的

我真的不明白CALayer的
显示和
DrawInText
与视图中的
drawRect
有什么关系

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

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

如果删除空的
display
方法并添加空的
drawInContext
方法,则永远不会调用
drawinrect
Update:但每隔1秒调用一次
drawInContext
,如NSLog语句所示

到底发生了什么?似乎
display
可以有选择地调用
drawInContext
drawInContext
可以有选择地调用
drawinrect
(如何?),但这里的真实情况是什么


更新:答案还有更多线索:

我将
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];
}
所以,比方说,如果在视图中的位置(100100)有一个月亮(由核心图形绘制的圆圈),现在我将其更改为位置(200200),自然,我将调用
[self.View setNeedsDisplay]
,现在,CALayer将根本没有新视图图像的缓存,正如我的
drawRect
指示了月亮现在应该如何显示

即使如此,入口点仍然是CALayer的
显示
,然后是CALayer的
drawInContext
:如果我在
drawRect
设置断点,调用堆栈将显示:

因此我们可以看到,首先输入了CoolLayer的
显示
,然后进入CALayer的
显示
,然后进入CoolLayer的
绘图文本
,然后进入CALayer的
绘图文本
,即使在这种情况下,新图像不存在这样的缓存

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

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


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

UIView的更新过程基于
dirty
状态,这意味着如果视图的外观没有变化,则不可能重新绘制视图


这是开发人员参考中提到的内部实现。

当需要显示一个层并且没有有效的备份存储时(可能是因为该层收到了
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;
}
- (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];
        }
    }
}
- (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);
}
因此,如果覆盖
显示
,除非调用
[super display]
,否则所有这些都不会发生。如果在
FooView
中实现
displayLayer:
,则必须以某种方式创建自己的
CGImage
,并将其存储在层的
内容
属性中

-[CALayer drawInContext:
方法大致如下:

- (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;
}
- (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];
        }
    }
}
- (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);
}
据我所知,
onDraw
操作没有文档记录

-[UIView drawLayer:inContext:
方法大致如下所示:

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

实现drawInContext或display或drawRect会告诉操作系统在视图脏时要调用哪一个(需要显示)。选择一个你想要的脏视图调用并实现它,不要将任何你依赖的代码放在其他视图中执行。

CALayer是如何发挥作用的?文档中明确提到,
setNeedsDisplay
仅适用于UIKit和Core图形图形,我相信脏视图更新对它们都是有效的。请注意,正如文档所述,这对
CaeAglayer
无效。问题是,如果我们使用
setNeedsDisplay
,但覆盖了CALayer的方法,那么
drawRect
不会被调用,为什么调用
setNeedsDisplay
会使
drawRect
不总是
(!)被调用-即使您没有覆盖它。这就是NSView绘图的优化方式。我认为,如果我们调用
setNeedsDisplay
,则通常会调用
drawRect
,或者,如果您正在编写游戏,则会调用