1

Since I have to draw many points in a UIview, I'd like to display an activity indicator view over the UIView.

I tried something like this, but it doesn't work.

-(void) drawRect:(CGRect)rect
{
     [self.activityIndicatorView startAnimating];

     CGContextRef cr = UIGraphicsGetCurrentContext();
     // Lots of CG functions (take a few seconds)

     [self.activityIndicatorView startAnimating];
}

What can I do to show a spinning wheel while drawing?


Edit with a solution (thanks @dasblinkenlight):

Two UIImageViews, one to be used as a placeholder (where the UIActivityIndicatorView runs), and another one that draws the CG content, named Renderer.

As read here the Renderer class writes a UIImage. Placing the drawrect call in another queue (background) when it finishes the placeholder can use the image to be displayed.

That's the code:

dispatch_queue_t myQueue = dispatch_queue_create("MyQueue", 0);

dispatch_async(myQueue, ^{
    [self.renderer drawRect:self.placeholder.bounds];

    dispatch_async(dispatch_get_main_queue(), ^{
        [self.placeholder setImage:[self.renderer renderedImage]];
    });
});

I stil can't understand why I have to call directly the drawRect function, instead of the setNeedDisplay. Is maybe because it is running in background?

Community
  • 1
  • 1
boskaiolo
  • 128
  • 7
  • 2
    You could draw into an image in a background thread and then just draw the image onto the view when done... Or you can use CATiledLayer which draws itself on a background thread, and even gives you free high-quality zooming when in a UIScrollView... – jjv360 Sep 04 '12 at 20:26

1 Answers1

1

You cannot do that: while your drawRect is executing, no other updates to the screen are possible; the spinning gear will not get a chance to be drawn or updated.

If your drawRect takes a few seconds, you should reconsider your strategy: perhaps you could isolate the drawing-intensive code in a function that draws to an in-memory bitmap, show a UIImageView with a placeholder image in place that was to be painted by your drawRect code, do the rendering in a background thread, and replace the image of the UIImageView with what your code has painted.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • So, 2 UIViews: the first will contain my "content" when it is drawn, and one used as a placeholder, right?Can you provide me a few hints about how to draw the bitmap image in background? – boskaiolo Sep 04 '12 at 21:31
  • @user1647181 No, only one `UIImageView`, but with two images. The first image is a static placeholder: add it as a resource, and display when the view is first loaded. The second image is your dynamic content. Take a look at [this answer](http://stackoverflow.com/a/12235753/335858) for drawing to a bitmap. Insert your code between lines one and two. Call this code using `dispatch_async` when the view shows up. In the code block that you pass to `dispatch_async`, add a line that sets the image of the `UIImageView` to the one produced by your drawing routine. This should do the trick. – Sergey Kalinichenko Sep 04 '12 at 21:47
  • Thanks! But I think I messed up something. – boskaiolo Sep 04 '12 at 22:47
  • I created 2 UIImaveView on the storyboard. One is the placeholder, one the "uiimage renderer". When the user press the button, i launch `dispatch_async(myQueue, ^{[self.renderer setNeedsDisplay]; dispatch_async(dispatch_get_main_queue(), ^{[self.placeholder setImage:[self.renderer imageCreated]]; }); });` What is wrong here? The drawrect function of the renderer object is never called. – boskaiolo Sep 04 '12 at 23:00
  • @user1647181 The code in the comments is hard to read, you may want to edit the question and include your code; it would be easier to see what is going on. – Sergey Kalinichenko Sep 04 '12 at 23:38
  • Thanks a lot, I'm almost done. I still don't understand why I have to call the drawRect function, instead of the setNeedDisplay. Any hint? – boskaiolo Sep 04 '12 at 23:58
  • @user1647181 The renderer class in your code does not need to inherit `UIImageView` at all: it could be a normal special-purpose class that inherits `NSObject`. All you need from that class is a method to render your picture. All it needs is a `render` method and a `renderedImage` property to harvest the result. You happened to put the rendering logic in the `drawRect` method, but you can choose any name for that method. – Sergey Kalinichenko Sep 05 '12 at 00:15