我首先想知道为什么我们将UIView
子类化,并且我们实现了一个drawRect
方法来调用,但同时,如果我们在这个视图中添加按钮或标签,并调整它们位置坐标,那些也将被重新绘制...所以这个视图似乎有一个双向绘制机制,一个用于自身,一个用于此视图中的对象。
但事实证明,似乎每隔1/60秒,整棵树就被遍历了。从顶视图对象开始,iOS将访问所有子项,然后为drawRect
调用self
,同样访问子项,访问孙子,{{1}为每个第一级孩子调用},如:
drawRect
并且每1/60秒拨打一次电话
-(void) processViewObject:(UIView *) obj {
// pseudo code:
foreach children "c" already sorted by zOrder from most negative to -1
processViewObject(c) // recursion
if ([self needsUpdateOrNot] == YES)
[self drawRect]
foreach children "c" already sorted by zOrder from 1 to the greatest number
processViewObject(c) // recursion
}
这样任何视图都可以在processViewObject(topViewObject); // start from the topmost view object
中绘制自己,并且视图的子节点,如果修改了它们的任何位置坐标,或者如果它们的内容被更改,那么脏位drawRect
已被设置早些时候,他们的needsUpdateOrNot
将被调用以重新绘制自己。
或者实际上,如果设置了一个视图的脏位,那么这个位可能会在递归中传递给子节点,如:
drawRect
因此,如果设置了父级的脏位,则还需要重新绘制子项,以便正确绘制此视图中的整个图片。
此外,每个漂亮的按钮或标签都只是一个 foreach children "c" already sorted by zOrder from 1 to the greatest number
processViewObject(c, [self needsUpdateOrNot])
对象,其中已经实现了UIView
,可以绘制出漂亮的图像 - 按钮,复选框,标签或任何其他小部件。
这是如何在屏幕上绘制所有内容的整体机制?我听说“永远不要打电话给drawRect
,但要让它被称为”,但是从来没有完全理解为什么,但如果以上是整体机制,那么看起来就是这样的原因。
我想知道这是否与微软Windows或任何GUI操作系统上的实际几乎相同的机制,并且在某些游戏框架上,我认为无论是否设置了脏位,所有内容都被绘制,因为据说,游戏中的东西一直在移动,而框架只需要每隔1/60秒抽取所有这些东西。
上面的drawRect
处理顺序是在某个框架的代码中,因此首先绘制所有负zOrder子项,然后绘制zOrder
,然后绘制所有正zOrder子项。 (这就是zOrder如何以画家模型的方式在屏幕上绘制东西 - 后者绘制将覆盖之前绘制的东西。)因此self
是self
的zOrder,如果相对于孩子 - 0
的zOrder数量,如果它与自己的兄弟姐妹有关,而不是与其子女有关。
上述准确吗?如果给出答案,如果可能的话,可以引用部分源代码或引用,以便我们知道这是它的工作方式的标准或官方方式吗?
答案 0 :(得分:6)
我认为你把绘画的合成混淆了。这两个是不同的概念,它们在不同的点上发挥作用。
在iOS上,每个UIView都有一个CALayer支持,它实际上是一个围绕矩形OpenGL ES纹理的轻量级包装器。当需要完全重绘UIView时(第一次出现,或者在不同情况下强制重绘时),会触发-drawRect:
并使用Core Graphics将矢量图形渲染到位图中。然后,该位图通过CALayer上传到GPU并存储在那里。
当需要移动视图或对其应用简单变换(旋转,缩放等)时,不会重新绘制视图,只需移动背衬层并合成可见场景中的图层。同样,每个视图和子视图都有自己的后备层,因此它们在视图(层)层次结构中以Z顺序合成。在GPU上进行合成是一种非常快速的操作,远远快于初始绘制视图。这是在iOS中实现平滑滚动或动画的原因,因为这些底层矩形图像只是在GPU上移动并合成。这也是为什么在视图和图层中使用透明度会减慢滚动等操作的原因,因为合成非不透明图层的速度明显慢于不透明图层。
当您移动子视图或位于其上方的其他视图时,不会重绘视图的重叠部分,因为合成会处理可见和不可见的内容。实际-drawRect:
重绘的唯一情况是,当您使用-setNeedsDisplay
强制重新渲染时,或者如果您为CALayer设置needsDisplayOnBoundsChange
,以便重绘自身以响应更改大小或形状。
我认为jturton链接到的文档部分在iOS上或Mac上的图层支持视图时是不正确的。它似乎是在Mac缺少Core Animation时编写的,并且是非层支持的NSView呈现的方式,因此它可能是从较旧的引用中复制和粘贴的。对于图层支持的视图不是这种情况,这些视图在部分遮挡时不会重绘。您可以通过在支持图层的UIView中调用-drawRect:
时进行日志记录来自行测试,并且当视图简单地相互移动时,您会看到它没有被触发。
答案 1 :(得分:2)
从底部(z-wise)向上绘制视图。对于不透明视图等有各种优化。所有标记为脏的视图都在运行循环结束时重绘 - 您怀疑没有每1/60秒机制。
答案 2 :(得分:1)
来自jrturton引用的documentation
在iOS和Mac OS X中绘图遵循以下两种技术路径之一 基于视图按需更新其外观的模型
我认为这将解决定期更新观点的问题。视图只能按需更新,这是可以理解的,因为对于它的所有处理能力,iphone仍然是一个嵌入式设备。处理器/内存越少越好..它也必须考虑电池寿命..
更新视图的一些情况(来自文档 再次)
- 用户移动或移除部分遮挡另一个视图的视图。
- 用户从包含滚动视图滚动视图,然后将其向上滚动。
- 代码通过将其隐藏属性设置为NO来再次显示先前隐藏的视图。
- 代码显式使视图无效若要使视图无效,并将其标记为重绘,则调用setNeedsDisplay或
方法 setNeedsDisplayInRect:在视图上。在主事件循环的循环结束时,窗口继续向下 它的视图层次结构并请求这些视图来绘制自己 调用他们的drawRect:方法。
因此,在主事件循环中,窗口(即父btw)遍历整个树层次结构,但仅更新需要更新的子项。其他视图保持不变..