10

我正在开发一个使用 CoreGraphics 进行渲染的绘画应用程序。我的问题是,为了性能,我试图将更新限制在视图中已更改的某些部分。为此,我使用 setNeedsDisplayInRect:,但有时视图会更新其全部内容,从而导致卡顿。这在第 3 代 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 (我的自定义视图):

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

就是这样。如果我在第 3 台 10 台 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}}
4

4 回答 4

13

接受的答案是错误的(或至少具有误导性)。

这是一个简单的 setNeedsDisplayInRect 代码示例:按照宣传的方式工作:

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

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

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

于 2013-05-31T08:47:18.467 回答
5

检查这个苹果文件

他们说 :

-setNeedsDisplayInRect:由于 iPhone/iPod touch/iPad 更新屏幕的方式,如果您调用或 ,整个视图将被重绘-setNeedsDisplay:

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

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

于 2012-05-10T07:36:25.213 回答
0

我有一个类似的问题:即只想通过调用 setNeedsDisplayInRect 来更新 UIView 的一部分:我传递了正确大小的有效 CGRect(通过断点和日志证明)。在 iOS 4.2.1 下(在我的 iPhone 3G 上),UIView 保留其原始图像,只更新由 CGRect 指定的部分,但在 iOS 5.1 下(在我的 iPhone 4S 上),整个 UIView 被重绘为透明黑色(如何黑色可以透明超出了我的范围),之后正确绘制了 CGRect 指定的区域。似乎是:Apple 的一个错误是他们没有正确实现 setNeedsDisplayInRect: 并且只是将它传递给 setNeedsDisplay:; 或者这是一个基于新 iOS 如何处理与实际屏幕架构交互的故意决定。无论哪种方式,

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

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

我会继续深入研究这一点,但我担心在 Apple 披露他们对 setNeedsDisplayInRect: 进行更改的理由或修复明显的错误之前,我们可能会考虑为每次图形更新更新 UIView 的全部内容(这可能可能非常“昂贵”)。

于 2012-05-11T16:32:12.057 回答
0

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

自iOS6以来我还没有尝试过。所以我不确定它是否在 iOS7 中可用。也许其他人可以确认。

请和我一起为按记录运行而无休止地欢呼setNeedsDisplayInRect:

于 2014-12-08T14:37:37.807 回答