32

我面前有 4 种不同的iPhone/Cocoa/Core Animation/Objective-C书籍,以及来自网络的大量示例代码。然而不知何故,我仍然觉得我缺少对Quartz 2D中绘图工作原理的一些基本理解。

是否drawRect()只是一个执行绘图代码的钩子?或者这种方法是否应该也重绘“损坏”的区域,需要重绘?我可以只画一次然后它“粘”起来,还是我必须随时通过重新绘制整个场景drawRect()?Java 的 Graphics2D 对象以这种方式工作 - 每次调用 paint() 时都必须绘制整个“图像”,因此您必须随时准备重新构建它(或缓存它)。

你将如何实现一个简单的绘图程序?您是否必须“记住”用户绘制的每条线/点/笔划,并在每次drawRect()调用时复制它们?“离屏”渲染怎么样?你能画完所有的画,然后打电话[self setNeedsDisplay]让你的写入刷新到屏幕上吗?

假设为了响应用户的触摸,我想在他触摸的屏幕上放置一个“X”。X 应该保留在那里,并且每次新的触摸都会产生另一个 X。我是否需要记住所有这些修饰坐标,然后将它们全部绘制出来drawRect()

编辑:

除非我误解了,否则 joconor 和 Hector Ramos 在下面的回答是相互矛盾的。这很好地证明了我对这个主题的困惑。:-)

4

4 回答 4

38

各种 Cocoa 引用之间的一些混淆来自于 Leopard 中层支持视图的引入。在 iPhone 上,所有 UIView 都是 layer-backing,而 Leopard 视图需要手动启用 layer-backing。

对于支持图层的视图,内容会使用您在 中提供的任何内容绘制一次drawRect(),然后缓冲到图层中。该层就像一个矩形纹理,所以当你移动或覆盖它时,不需要重绘,纹理只是通过 GPU 移动到那个位置。除非您将图层设置为needsDisplayOnBoundsChange property,否则YES更改图层(或其包含的视图)的大小只会缩放内容。这可能会导致您的视图或图层中的图形模糊,因此您可能希望在这种情况下强制重绘。setNeedsDisplay将触发视图或图层内容的手动重绘,以及随后在图层中重新缓存该内容。

为了获得最佳性能,建议您避免频繁调用drawRect,因为在图层中绘制和重新缓存 Quartz 是昂贵的操作。最好尝试使用可以移动或缩放的单独图层来制作动画。

您看到的与桌面相关的基于 Cocoa 的引用可能假定非图层支持的视图,它们确实调用 drawRect:任何时候需要更新视图,无论是来自移动、缩放还是拥有部分视图模糊不清。正如我所说,所有 UIView 都是有层支持的,所以 iPhone 上不是这种情况。

也就是说,对于您的绘图应用程序,一种方法是维护一个绘制对象数组并调用 drawRect:每次用户添加新内容时,按顺序迭代每个先前绘制的对象。我可能会建议另一种方法,为每个绘图操作创建一个新的 UIView 或 CALayer。该绘制操作的内容(线、弧、X 等)将由单个视图或图层绘制。这样,您就不必重新绘制所有内容,并且您可以通过独立移动每个绘制的元素来进行一些简洁的矢量样式编辑。对于复杂的绘图,这可能会有一些内存折衷,但我敢打赌它会有更好的绘图性能(最小的 CPU 使用率和闪烁)。

于 2009-04-04T15:11:05.977 回答
4

drawRect() 将绘制到屏幕外缓冲区。当区域“损坏”本身时,您无需重新绘制,因为 iPhone OS 负责处理视图的分层。您只需向缓冲区写入一次,然后让操作系统处理其余部分。这与其他编程环境不同,在其他编程环境中,只要有东西越过您的视图,您就需要继续重绘。

于 2009-04-04T04:26:59.193 回答
2

始终准备好在drawRect:调用时绘制视图的适当区域。

尽管系统可能会缓冲您的视图,但这只会避免drawRect:被调用。如果由于某种原因,系统必须使缓冲区无效,则drawRect:可能会再次调用您的方法。此外,drawRect:将在视图的不同区域因滚动和其他影响视图区域可见性的操作而变得可见时调用。

于 2009-04-04T05:14:01.297 回答
2

drawRect() 是否只是一个执行绘图代码的钩子?

它旨在使用当前图形上下文堆栈重绘传递给您的区域(矩形)。不再。

或者这种方法是否应该也重绘“损坏”的区域,需要重绘?

不会。如果脏区不重叠,您可能会收到多个调用,drawRect:其中传递给您的不同矩形。使用 使矩形无效setNeedsDisplayInRect:。如果只需要重新绘制视图表面的一部分,那么当需要绘制时,您将被要求绘制该部分。

我可以只画一次然后它“粘住”,还是必须随时通过 drawRect() 重新绘制整个场景?

它不“粘”。矩形在您的应用执行期间变得无效,并且当视图系统需要更新屏幕时,您需要重新绘制这些矩形。您只重绘请求的矩形。

在某些情况下,一个简单的实现可能(相当懒惰地)使视图的整个矩形无效,只要一部分无效。这通常很糟糕,因为它通常需要比必要更多的绘图,并且当视图不透明时尤其浪费。

Java 的 Graphics2D 对象以这种方式工作 - 每次调用 paint() 时都必须绘制整个“图像”,因此您必须随时准备重新构建它(或缓存它)。

AppKit 或 UIKit 并非如此。

你将如何实现一个简单的绘图程序?您是否必须“记住”用户绘制的每条线/点/笔划,并在每次调用 drawRect() 时复制它们?

您必须记住绘制视图所需的上下文(例如,每条线/点/笔划)。您只需要绘制所请求的区域。从技术上讲,如果您要在该矩形之外绘制,图形系统不会抱怨,但这可能会导致伪影。

对于复杂的渲染,绘制到外部缓冲区(例如位图)可能更容易或更有效,然后使用预渲染的位图表示在drawrect:. (另请参阅 Brad 对图层的回答)

“离屏”渲染怎么样?你能完成所有的绘图然后调用 [self setNeedsDisplay] 将你的写入刷新到屏幕上吗?

是的,你可以这么做。具体来说,您将渲染到外部缓冲区(例如位图),当您完成对位图的渲染时,使您希望绘制的矩形无效,然后在drawRect:调用时使用位图中的数据绘制到屏幕。

假设为了响应用户的触摸,我想在他触摸的屏幕上放置一个“X”。X 应该保留在那里,并且每次新的触摸都会产生另一个 X。我是否需要记住所有这些修饰坐标,然后将它们全部绘制在 drawRect() 中?

好吧,你有几个选择。您的建议是一个(假设您在传递给您的矩形内绘制)。另一种方法是创建一个“X”视图,如果您需要这些 X 在启动过程中持续存在,只需记住重建视图所需的点即可。在许多情况下,您可以轻松地将复杂的问题分成几层(简单的 2D 游戏):

  • 1)具有地平线的背景图像。
  • 2)前台的一些东西不经常改变。
  • 3) 用户用于在游戏中导航的角色。

因此,大多数问题都可以轻松划分,因此您不必一直渲染所有内容。如果做得好,这会降低复杂性并提高性能。如果做得不好,可能会更糟。

于 2011-10-21T20:14:30.073 回答