我正在使用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台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}}
答案 0 :(得分:13)
接受的答案错误(或至少具有误导性)。
这是setNeedsDisplayInRect的一个简单代码示例:按照宣传的方式工作:
http://charcoaldesign.co.uk/resources/chalkboard.zip
我怀疑Apple文档试图解释这样一个事实:在iOS上,每个视图支持图像都转换为OpenGL纹理,并且每个帧都重新合成整个屏幕。这与您的代码是否必须清除和重绘视图的支持纹理的整个内容的问题不同。
通过使用setNeedsDisplayInRect:您可以避免昂贵的重绘内容,这些内容在帧之间不会发生变化(使用核心图形而不是硬件加速)。无论如何,整个屏幕仍然会被重绘,但这并不重要,因为GPU的硬件加速,与drawRect中的代码不同:
答案 1 :(得分:5)
检查此苹果document
他们说:因为iPhone / iPod touch / iPad更新其屏幕的方式, 如果您拨打
-setNeedsDisplayInRect:
或,则会重新绘制整个视图-setNeedsDisplay:
。
另外
每个UIView都被视为一个单独的元素。当您通过致电
-setNeedsDisplayInRect:
或-setNeedsDisplay:
部分或全部请求重绘时,整个视图将被标记为更新。
所以我认为您需要使用子视图来更新整个View的独立rect。
答案 2 :(得分:0)
我有一个类似的问题:即想要通过调用setNeedsDisplayInRect来更新UIView的一部分:我传递了一个正确大小的有效CGRect(用Break Points和Logs证明)。在iOS 4.2.1(在我的iPhone 3G上),UIView保留其原始图像并仅更新CGRect指定的部分,但在iOS 5.1(在我的iPhone 4S上)整个UIView以透明黑色重绘(黑色可以是多少)透明度超出我的范围,CGRect指定的区域在之后正确绘制。它似乎要么是:Apple的一个错误,他们没有正确实现setNeedsDisplayInRect:只是将它传递给setNeedsDisplay:;或者这是一个根据新iOS如何处理与实际屏幕架构的交互的故意决定。无论哪种方式,似乎目前开发人员要么每次发生图形更改时都要承担更新整个UIView的成本,要么使用一些iOS检测并根据所处理的iOS实现不同的屏幕绘图代码。
我已经使用UIView属性clearsContextBeforeDrawing进行了一些测试(根据文档应该允许有针对性的绘图操作),但结果对于YES和NO都是相同的,所以它似乎是绘图代码的实现。
对于更新期间CGRect的大小不正确:是否在绘图调用之前或之后的其他位置设置。我在调用setNeedsDisplayInRect:之前设置了我的CGRect,然后在drawRect方法的最后几行将它设置为CGRectNull(如果系统调用drawRect方法,我的代码将不会触发)。回到你的代码片段之后,我可以看到它被设置为内联,这意味着它是一个系统调用,它重绘了整个UIView,因此CGRect是整个视图的原因。
我会继续深入研究这个问题,但我担心,直到Apple公布他们改变setNeedsDisplayInRect:的理由,或修复明显的bug,我们可能会考虑更新UIView的所有内容以进行每次图形更新(这可能非常'昂贵')。
答案 3 :(得分:0)
我没有很多可用的设备,但无论如何我都有好消息!作为多年来一直希望在iOS上使用此功能的人,我可以断言iOS8.0 +带来了这一功能。我已经尝试过8.0,8.1,8.2的模拟器和设备
我从iOS6开始就没试过这个。所以我不确定它是否可以在iOS7中使用。也许其他人可以证实。
请与我一起欢呼,setNeedsDisplayInRect:
按照记录的方式运作!