setNeedsDisplayInRect:导致整个视图被更新

我正在使用CoreGraphics进行渲染的绘画应用程序。 我的问题是,为了提高性能,我试图限制只更新视图的某些部分已经改变。 为此,我使用setNeedsDisplayInRect :,但有时视图更新其整个内容,导致口吃。 这在第三代iPad上尤其明显,但在模拟器和iPad2中也会发生较小的变化。 我正试图消除这种行为。

为了展示这个问题,我使用Xcode中的模板创build了一个简单的“单一视图”项目。 创build一个自定义的UIView子类,我将其设置为xib文件中的视图控制器的视图。

将此添加到UIViewController:

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { // Asks that the view refreshes a smal rectangle [self.view setNeedsDisplayInRect:CGRectMake(10, 10, 20, 20)]; } 

将此添加到MyView(我的自定义视图)中:

 - (void)drawRect:(CGRect)rect { // Just log what is being updated NSLog(@"%@", NSStringFromCGRect(rect)); } 

就是这样。 如果我在第三十台iPa​​d(实际设备)上运行该应用程序,则日志会显示该视图时常重新绘制其自身(如非常频繁地)。 这太令人沮丧了,而且我没有想法

更新:这里有一些日志。 它绝对显示有时更新的全景。 我试图让它完全停止,但不能如何…

 2012-05-04 08:34:01.851 TestUpdateArea[45745:707] {{0, 0}, {320, 460}} 2012-05-04 08:34:30.184 TestUpdateArea[45745:707] {{0, 0}, {320, 460}} 2012-05-04 08:34:30.197 TestUpdateArea[45745:707] {{0, 0}, {320, 460}} 2012-05-04 08:34:30.215 TestUpdateArea[45745:707] {{10, 10}, {20, 20}} 2012-05-04 08:34:30.226 TestUpdateArea[45745:707] {{10, 10}, {20, 20}} 2012-05-04 08:34:30.242 TestUpdateArea[45745:707] {{10, 10}, {20, 20}} 2012-05-04 08:34:30.258 TestUpdateArea[45745:707] {{10, 10}, {20, 20}} 2012-05-04 08:34:30.274 TestUpdateArea[45745:707] {{10, 10}, {20, 20}} 2012-05-04 08:34:30.290 TestUpdateArea[45745:707] {{10, 10}, {20, 20}} 2012-05-04 08:34:30.306 TestUpdateArea[45745:707] {{10, 10}, {20, 20}} 2012-05-04 08:34:30.322 TestUpdateArea[45745:707] {{10, 10}, {20, 20}} 2012-05-04 08:34:30.338 TestUpdateArea[45745:707] {{10, 10}, {20, 20}} 2012-05-04 08:34:30.354 TestUpdateArea[45745:707] {{10, 10}, {20, 20}} 2012-05-04 08:34:30.371 TestUpdateArea[45745:707] {{10, 10}, {20, 20}} 2012-05-04 08:34:30.387 TestUpdateArea[45745:707] {{10, 10}, {20, 20}} 2012-05-04 08:34:30.403 TestUpdateArea[45745:707] {{10, 10}, {20, 20}} 2012-05-04 08:34:30.419 TestUpdateArea[45745:707] {{10, 10}, {20, 20}} 2012-05-04 08:34:30.439 TestUpdateArea[45745:707] {{0, 0}, {320, 460}} 2012-05-04 08:34:30.457 TestUpdateArea[45745:707] {{10, 10}, {20, 20}} 

此外,如果我停止移动超过10秒左右,并恢复,这绝对是非常一致的:

 2012-05-04 08:34:33.305 TestUpdateArea[45745:707] {{10, 10}, {20, 20}} 2012-05-04 08:34:33.321 TestUpdateArea[45745:707] {{10, 10}, {20, 20}} 2012-05-04 08:35:00.202 TestUpdateArea[45745:707] {{0, 0}, {320, 460}} 2012-05-04 08:35:00.221 TestUpdateArea[45745:707] {{0, 0}, {320, 460}} 2012-05-04 08:35:00.234 TestUpdateArea[45745:707] {{0, 0}, {320, 460}} 2012-05-04 08:35:00.251 TestUpdateArea[45745:707] {{10, 10}, {20, 20}} 

检查这个苹果文件

他们说 :

由于iPhone / iPod touch / iPad更新其屏幕的方式,如果您调用了-setNeedsDisplayInRect:-setNeedsDisplay:则整个视图都将重新绘制。

每个UIView被视为一个单一的元素。 当您通过调用-setNeedsDisplayInRect:-setNeedsDisplay:来部分或全部请求重绘时,整个视图将被标记为更新。

所以我认为你需要使用子视图来更新整个视图的独立矩形。

接受的答案是错误的 (或者至less是误导性的)。

下面是一个setNeedsDisplayInRect的简单代码示例:按照广告方式工作:

http://charcoaldesign.co.uk/resources/chalkboard.zip

我怀疑苹果的文档是试图解释的事实,在iOS上,每个视图支持图像被转换为​​OpenGL纹理,整个屏幕重新合成每一帧。 这与您的代码是否必须清除并重绘视图的背景纹理的全部内容的问题截然不同。

通过使用setNeedsDisplayInRect:可以避免昂贵的内容重绘,这些内容在帧之间不会改变(使用核心graphics,而不是硬件加速)。 无论如何,整个屏幕仍然会被重新绘制,但这并不重要,因为它是由GPU加速的硬件,与drawRect中的代码不同:

我有一个类似的问题:即想通过调用setNeedsDisplayInRect只更新一个UIView的一部分:我通过一个有效的CGRect的正确大小(断点和日志certificate)。 在iOS 4.2.1下(在我的iPhone 3G上),UIView保持其原始图像,只更新由CGRect指定的部分,但在iOS 5.1(在我的iPhone 4S上),整个UIView以透明黑色重新绘制透明超越了我),CGRect指定的区域被正确绘制之后。 它似乎是:苹果的一个错误,他们没有实现setNeedsDisplayInRect:正确,只是传递给setNeedsDisplay:; 或者是基于新的iOS如何处理与实际屏幕架构的交互而做出的有意决定。 不pipe怎样,看起来目前开发人员要么每当发生graphics变化时都要花费更新整个UIView的成本,要么使用一些iOS检测,并根据所处理的iOS实现不同的屏幕绘制代码。

我已经使用UIView属性clearsContextBeforeDrawing(根据文档应该允许有针对性的绘制操作)进行了一些testing,但是对于YES和NO,结果都是一样的,所以它看起来就是绘图代码的实现。

至于CGRect在更新过程中大小不正确:在绘图调用之前还是之后,是否将其设置在其他位置? 我在调用setNeedsDisplayInRect之前设置了CGRect,然后在drawRect方法的最后几行将其设置为CGRectNull(这是因为如果系统调用drawRect方法,我的代码不会触发)。 回到你的代码片段后,我可以看到它被内联设置,这意味着这是一个重绘整个UIView的系统调用,因此CGRect为什么是整个视图。

我会继续深入研究这个问题,但是我担心,除非Apple公开他们对setNeedsDisplayInRect的更改的理由,否则修复这个明显的错误,我们可能会更新每个graphics更新的UIView的全部内容可能非常“昂贵”)。

我没有很多设备,但是我有好消息! 作为一个多年来一直希望在iOS上使用这个function的人,我可以断言iOS8.0 +带来了这个function。 我已经在模拟器和设备上都试过了8.0,8.1,8.2

自从iOS6以来,我一直没有尝试过。 所以我不确定它是否在iOS7中可用。 也许别人可以证实。

请与我一起欢呼,无尽地为setNeedsDisplayInRect:欢呼setNeedsDisplayInRect:按照logging运作!