Ios setNeedsDisplayInRect:导致更新整个视图

Ios setNeedsDisplayInRect:导致更新整个视图,ios,core-animation,core-graphics,Ios,Core Animation,Core Graphics,我正在开发一个使用CoreGraphics进行渲染的绘画应用程序。我的问题是,为了提高性能,我试图将更新限制在视图中已更改的某些部分。为此,我使用setNeedsDisplayInRect:,但有时视图会更新其全部内容,导致口吃。这在第三代iPad上尤其明显,但在模拟器和iPad2上也会发生,程度较低。我正试图消除这种行为 为了展示这个问题,我使用Xcode中的模板创建了一个简单的“单视图”项目。创建了一个自定义UIView子类,我在xib文件中将其设置为视图控制器的视图 将此添加到UIView

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

为了展示这个问题,我使用Xcode中的模板创建了一个简单的“单视图”项目。创建了一个自定义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(我的自定义视图):

就这样。如果我在Third ten iPad(实际设备)上运行该应用程序,日志会显示视图不时地重新绘制自己的整体(非常频繁)。这太令人沮丧了,我已经没有主意了

更新:这里有一些日志。它肯定会显示有时更新的完整视图。我试图让它完全停止,但不知道如何

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:
-设置需要显示:

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


所以我认为您需要使用子视图来更新整个视图的独立rect

我有一个类似的问题:即只想通过调用setNeedsDisplayInRect来更新UIView的一部分:我传递了一个正确大小的有效CGRect(用断点和日志证明)。在iOS 4.2.1(在我的iPhone 3G上)下,UIView保留其原始图像,只更新CGRect指定的部分,但在iOS 5.1(在我的iPhone 4S上)下,整个UIView以透明的黑色重新绘制(黑色的透明程度超出我的想象),之后正确绘制CGRect指定的区域。这似乎是:苹果方面的一个错误,他们没有正确地实现setNeedsDisplayInRect:并将其传递给setNeedsDisplay:;或者,这是一个基于新iOS如何处理与实际屏幕架构交互的故意决定。无论是哪种方式,目前看来,开发者要么不得不承担每次图形更改时更新整个UIView的成本,要么使用一些iOS检测并根据所处理的iOS实现不同的屏幕绘制代码

我已经使用UIView属性ClearContextBeforeDrawing做了一些测试(根据文档,它应该允许有针对性的绘图操作),但是结果是相同的,所以它似乎是绘图代码的实现

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


我会继续深入研究这一点,但我担心,除非苹果公司披露他们更改setNeedsDisplayInRect:的理由,或者修复明显的错误,否则我们可能会考虑为每次图形更新更新UIView的全部内容(这可能非常“昂贵”)。

公认的答案是错误的(或至少具有误导性)

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

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


通过使用setNeedsDisplayInRect:您可以避免昂贵的内容重画,这些内容在帧之间不会改变(使用核心图形,并且不是硬件加速的)。不管怎样,整个屏幕仍将被重新绘制,但这并不重要,因为它是由GPU加速的硬件,与drawRect中的代码不同:

我没有很多设备可用,但我有一个好消息!作为多年来一直希望在iOS上实现这一功能的人,我可以断言iOS8.0+带来了这一乐趣我在模拟器和设备上都试过8.0、8.1、8.2

我从iOS6开始就没有尝试过这个。所以我不确定它是否在iOS7中可用。也许其他人可以确认


请和我一起为
setNeedsDisplayInRect:
如文件所述的功能而欢呼吧!

在我的情况下,当第一次在setNeedsDisplayInRect之后调用drawRect时,rect已经满了(不要问我为什么),任何下一次调用它都是我在setNeedsDisplayInRect中指定为参数的rect。您是否多次测试触摸移动?它主要直接发生在设备上。但如果您移动手指,然后等待大约10或15秒,然后再次触摸并移动,则可以在模拟器上实现。在iPad3设备上,它可能会甚至在手指移动的时候也可以使用钢笔。太烦人了:)我不久前发布了@this()。这似乎只是第一次打电话。我
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}}