在iOS上,setNeedsDisplay实际上并不';t导致调用drawRect。。。除非CALayer';s display或drawInContext最终调用drawRect?
我真的不明白CALayer的在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方法并添加空的
显示和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
,或者,如果您正在编写游戏,则会调用