Quartz 2D drawRect方法(iPhone)

我有4种不同的iPhone / Cocoa / Core Animation / Objective-C书籍,还有很多来自networking的示例代码。 然而,不知何故,我仍然觉得我错过了一些关于如何在Quartz 2D中进行绘图的基本知识。

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

你将如何实现一个简单的绘图程序? 你将不得不“记住”用户绘制的每一行/点/笔画,并且每次调用drawRect()drawRect()复制这个笔画? 如何“离屏”渲染; 你可以做所有的绘画,然后调用[self setNeedsDisplay]让你的写入刷新到屏幕上?

让我们说,为了响应用户的触摸,我想在他触摸的屏幕上放置一个“X”。 X应保持在那里,每个新的触摸产生另一个X.我是否需要记住所有这些触点坐标,然后将它们全部绘制在drawRect()

编辑:

除非我误解,joconor和Hector Ramos的回答是相互矛盾的。 这很好地certificate了我对这个问题的困惑。 🙂

一些cocoa参考之间的混淆来自Leopard中引入的层次支持视图。 在iPhone上,所有的UIView都是分层的,在Leopard视图中需要手动启用分层。

对于层次支持的视图,使用drawRect()提供的任何内容绘制内容,然后缓冲到图层中。 该图层的作用类似于矩形纹理,因此,当您移动支持层的视图或覆盖该视图时,不需要重绘,纹理仅通过GPU移动到该位置。 除非您将needsDisplayOnBoundsChange property设置为YES ,否则更改图层(或其包含的视图)的大小将简单地缩放内容。 这可能会导致您的视图或图层中模糊的graphics,因此您可能需要在这种情况下强制重绘。 setNeedsDisplay将触发视图或图层内容的手动重绘,并随后在图层中重新播放该内容。

为了获得最佳性能,build议避免频繁调用drawRect ,因为在一个图层中绘制Quartz和recaching是昂贵的操作。 最好尝试使用可以移动或缩放的单独图层进行animation。

您看到的与桌面相关的基于Cocoa的引用可能会假设非层支持的视图,这些视图会调用drawRect:任何时候视图需要更新,无论是从移动,缩放还是视图的一部分模糊。 正如我所说,所有的UIViews都是层层支持的,所以在iPhone上并不是这样。

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

drawRect()将绘制到屏幕外的缓冲区。 随着iPhone OS负责处理视图的分层,您不需要随时重绘区域“受损”的区域。 你只需要写一次缓冲区,然后让操作系统处理剩下的事情。 这与其他编程环境不一样,只要有东西通过视图就需要重绘。

drawRect:被调用时,始终要准备绘制视图的相应区域。

虽然系统可能会缓冲你的视图,但这只会避免drawRect:被调用。 如果由于某种原因,系统必须使缓冲区无效,那么可能会再次调用drawRect:方法。 此外, drawRect:将被调用的视图的不同区域,因为滚动和其他操作会影响视图区域的可视性。

drawRect()意味着只是一个钩子来执行您的绘图代码?

这意味着使用当前的graphics上下文堆栈重新绘制传递给您的区域(rect)。 不再。

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

不可以。如果脏区域不重叠,可能会收到多个drawRect:调用drawRect:带有传递给您的不同反应。 使用setNeedsDisplayInRect:来使Reces失效。 如果只有一部分视图的表面必须重新绘制,那么在绘制时要求您绘制该部分。

我可以只画一次,然后“粘住”,或者我必须通过drawRect()随时重绘整个场景吗?

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

在某些情况下,只要一个部分失效,一个简单的实现可能(相当懒散地)使视图的整个矩形无效。 这通常是不好的,因为它通常需要比所需的更多的绘图,并且当视图不透明时特别浪费。

Java的Graphics2D对象是这样工作的 – 每次调用paint()时都必须绘制整个“图像”,因此您必须随时准备重新构build它(或caching它)。

与AppKit或UIKit不一样。

你将如何实现一个简单的绘图程序? 你将不得不“记住”用户绘制的每一行/点/笔画,并且每次调用drawRect()时都要复制这个笔画?

您将不得不记住绘制视图所需的上下文(例如每一行/点/笔划)。 您只需绘制请求的区域。 从技术上讲,如果你在外面绘制graphics系统,graphics系统不会抱怨,但这可能会导致文物。

对于复杂的渲染,绘制到外部缓冲区(例如位图)可能会更容易或更高效,然后使用该预渲染的位图表示法在drawrect:获取屏幕上的东西drawrect: 。 (另请参阅Brad的图层答案)

如何“离屏”渲染; 你可以做所有的绘画,然后调用[self setNeedsDisplay]让你的写入刷新到屏幕上?

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

让我们说,为了响应用户的触摸,我想在他触摸的屏幕上放置一个“X”。 X应保持在那里,每个新的触摸产生另一个X.我是否需要记住所有这些触点坐标,然后将它们全部绘制在drawRect()中?

那么,你有几个select。 您的build议是一个(假设您在传递给您的矩形内)。 另一种方法是创build一个“X”视图,并且只要记住重build视图所需的要点,如果需要这些X在启动时保持不变的话。 在很多情况下,你可以很容易地将复杂的问题分成几层(简单的2D游戏):

  • 1)与地平线的背景图像。
  • 2)有些事情在前台不经常改变。
  • 3)用户用来在游戏中导航的angular色。

所以,大多数问题可以很容易地分开,所以你不必一直渲染所有的东西。 如果做得好,这样可以降低复杂性并提高性能 如果做得不好,可能会更糟。