3

I have a draw area (UIView), where i draw within the CGContextRef.
Ofcourse i am catching touches to draw. While the drawRect method slowly draws lot's of existing objects the touches are not caught.
If i call drawing in drawRect in separate thread or with function "dispatch_async" it doesn't draw because it has no appropriate context.
I've seached a lot but found nothing.

dispatch_async(dispatch_get_main_queue(), ^{
    [_mainArea setNeedsDisplay];
});
  • these things do not help either.

What can i do? Can anybody help to catch touches and draw lots of objects simultainiously?

Pheu Verg
  • 230
  • 2
  • 12

1 Answers1

9

When you're having problems keeping your graphics up to date, before you start getting into multithreading...

  1. You should remove all calls to display or setNeedsDisplay and replace them with setNeedsDisplayInRect:

  2. You should optimize your drawRect: method so that you're only redrawing the parts of the view that need it. You can get even more fancy in this method and ignore the rect argument from the method and obtain the raw rects being drawn from getRectsBeingDrawn:count:.

  3. If you're drawing moving objects, use CoreAnimation.

  4. If you have a lot of repeated objects, use CGLayerRefs

Calling setNeedsDisplay with a dispatch_async() won't make the view draw on another thread; all drawing happens in drawRect:, and drawRect: is always called by the main thread/queue.

UIGraphicsGetCurrentContext() is not thread safe. What you can do however is create a bitmap context in your async block, stroke and fill into that, and then get this context as a CGImageRef that can be comp'd into the CGContextRef (the one that's talking to the screen) running on the main thread. It would look something like this:

@implementation BGDrawingClass : UIVew {
    CGImageRef bgImage;
}

-(void)prepBGImage {

CGRect rect = TheRectISpendALotOfTimeIn();
dispatch_async(_myBGQueue,^{

        CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
        CGContextRef ctx = CGBitmapContextCreate(nil, rect.size.width, rect.size.height, 8, rect.size.width * (CGColorSpaceGetNumberOfComponents(space) + 1), space, kCGImageAlphaPremultipliedLast);
        CGColorSpaceRelease(space);

        /* do expensive drawing in this context */

        bgImage = CGBitmapContextCreateImage(ctx);

        dispatch_async(dispatch_get_main_queue(),^{ 
             [self setNeedsDisplayInRect:rect];
        });
    });
}

-(void)drawRect:(NSRect)rect {
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    /* do drawing behind this ... */
    if (CGRectIntersectsRect(rect, TheRectISpendALotOfTimeIn()) {
        CGContextDrawImage(ctx, TheRectISpendALotOfTimeIn(), bgImage);
    }
    /* do drawing in front of this */
}
@end
iluvcapra
  • 9,436
  • 2
  • 30
  • 32
  • Thank you very much, it is a very good solution. (I thought about using second canvas, but i did not know about CGImage) But 1) I need two CGImages for simultainious drawing and redrawing, and copying image takes a lot of time (ok, i may handle that by using pointers); 2) this code has memory leaks. I tried to use CGImageRelease, but it doesn't help! What to do? And is there a way to replace the canvas of uiview with bitmap of CGImage? – Pheu Verg Jan 31 '13 at 11:26
  • Oh, it seams, that leaks were also caused by context. After i added CGContextRelease memory warnings stopped after long time drawing. – Pheu Verg Jan 31 '13 at 11:35
  • The next step would be profiling your drawing code to see where it's spending all that time. Do you know how many draw operations you're doing per redraw? – iluvcapra Jan 31 '13 at 17:36
  • Changing pointers causes "bad access" crash for unknown reason. Anyway lags now are constant relating only to imagesize. And the touches are processed well (except began and ended). But theses are other issues and other questions. Thank You. – Pheu Verg Feb 01 '13 at 07:04