0

Say, for an iOS app, if a user slides his finger on the screen, and then 50,000 dots are recorded. If the drawing is done for all these dots in drawRect, then next time the user touched the 50,001st dot, at the end of touchesMoved the following line

[self.view setNeedsDisplay];

will cause drawRect to run again and have all 50,001 dots drawn again. So for every 1 new dot (for any new movement of finger), all 50,001 dots will need to be redrawn and it is not an efficient method.

I tried just drawing the last dot in drawRect, and it will not "add to" the existing view, but "wipe everything out" and then draw one dot.

Is there a way to

1) draw that 1 extra dot without needing to have drawRect called?
2) or, can drawRect draw one extra dot without first wiping the whole screen out?

nonopolarity
  • 146,324
  • 131
  • 460
  • 740
  • 2
    Do not record all 50k dots but draw only the most recent ones within the refresh-call (drawRect). The other (say 49.9k) could be drawn directly to a bitmap which basically functions as a cache. So the bitmapped dots won't need to be redrawn one-by-one and can be blitted in one go. – Till Apr 21 '12 at 08:23

2 Answers2

1

Take a look at the method -(void)setNeedsDisplayInRect:. You can ask your view to redraw a specified rect of itself, but be careful when implementing drawRect: method -where you need to assume that the passed rect argument is just a piece of the whole rect of your view and probably your drawing logic will differ. Also you may consider the clearContextBeforeDrawing property of UIView.

graver
  • 15,183
  • 4
  • 46
  • 62
  • strange, I have put `self.clearsContextBeforeDrawing = NO;` in `TestView`'s `initWithFrame`, and in the ViewController before calling `[self.view setNeedsDisplay];` (using `self.view.clearsContextBeforeDrawing = NO;`) and the view is still cleared before anything is drawn – nonopolarity Apr 21 '12 at 16:52
  • What happens if you call setNeedsDisplayInRect: ? setNeedsDisplay generally means that you are going to redraw the whole view. – graver Apr 21 '12 at 17:08
  • the same... for simplicity I used `[self.view setNeedsDisplayInRect:CGRectMake(0, 0, 600, 600)];` and drew in the upper left 600 x 600 region – nonopolarity Apr 21 '12 at 17:40
1

One approach for this would be to render into an image when the touches end, and then keep adding to the image as more paths are generated. You can store the paths in an array if you need and undo buffer or otherwise need to regenerate the drawing.

(you will need more code than this, obviously)

 UIGraphicsBeginImageContextWithOptions(size, NO, 1.0);
 CGContextRef context = UIGraphicsGetCurrentContext();
 // do some drawing
 UIImage * image = UIGraphicsGetImageFromCurrentImageContext();
 UIGraphicsEndImageContext();

Oh - fwiw, I have found setNeedsDisplayInRect: to be odd/buggy in iOS5. The first call to it is the full view rect, not the rect passed in as a param. At least that is what I found when I tried to use it. Maybe there is some implementation detail I overlooked.

spring
  • 18,009
  • 15
  • 80
  • 160
  • 1
    I think drawRect: is called for the first time, when the view is first displayed with the whole rect as argument (called by UIKit, not your request with setNeedsDisplayInRect:) and that's an expected behavior. With setNeedsDisplayInRect: you only invalidate a portion of the already drawn view. – graver Apr 21 '12 at 08:48
  • Here is my original question on this: http://stackoverflow.com/questions/9299018/setneedsdisplayinrect-bug-in-ios5 - I was seeing the issue after the view was already loaded, and in a specific touch method. I saw something the other day in the docs @ "view is created when it is referenced" or something like that - so maybe the first time I reference it in code, the entire view is drawn? Seems like a bug though. – spring Apr 21 '12 at 14:21