Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

setNeedsDisplayInRect: causes the whole view to be updated

I'm working on a painting app that uses CoreGraphics for rendering. My problem is, for performance, I am trying to limit the updates to only certain portions of the view that have changed. For that, I use setNeedsDisplayInRect:, but sometimes the view updates its entire content, causing stuttering. This is particularly evident on the 3rd generation iPads, but it also happens in the simulator and the iPad2 to a lesser degree. I am trying to remove this behavior.

To showcase the problem, I created a simple "single view" project using the template in Xcode. Created a custom UIView subclass which I set as the view controller's view in the xib file.

Added this to the UIViewController:

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

Added this to the MyView (my custom view):

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

And that's it. If I run the app on the 3rd ten iPad (the actual device), the logs show the view repainting itself in its entierty from time to time (like very frequently). This is so frustrating and I am out of ideas

UPDATE: Here are some logs. It definitely shows the full view being updated sometimes. I am trying to make it stop completely but can't figure how to...

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}}

Also if I stop moving for over 10s or so and resume, it definitely does it very consistently:

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}}
like image 429
mprivat Avatar asked May 07 '12 14:05

mprivat


1 Answers

The accepted answer is wrong (or at least misleading).

Here's a simple code example of setNeedsDisplayInRect: working as advertised:

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

I suspect that the Apple documentation is trying to explain the fact that on iOS every view backing image is converted to an OpenGL texture and the entire screen is re-composited every frame. That distinct from the question of whether your code has to clear and redraw the entire contents of the view's backing texture or not.

By using setNeedsDisplayInRect: you can avoid expensive redrawing of content that doesn't change between frames (which uses core graphics and is not hardware accelerated). The whole screen will still be redrawn regardless, but that doesn't matter because it's hardware accelerated by the GPU, unlike the code in drawRect:

like image 74
Nick Lockwood Avatar answered Nov 15 '22 19:11

Nick Lockwood