Pregunta

I have an IOS application that needs to update a view in response to either user or external events. The drawing time can be either very short or very long (several seconds) depending upon what is in the view.

Right now, the drawing takes place in the drawRect method for the view When the drawing is long and there is a lot of user interaction the app becomes unresponsive.

When it needs updating, the entire view needs to be updated. If a change comes in the middle of drawing, it would make sense to restart the drawing to take into account the changes.

I would like to do the drawing in the background.

My question is what would be the best mechanism for doing this in IOS?

Thread, NSOperation?

How do I handle the drawing? For example, how do I get a graphics context in the thread for the view I am updating?

How should the drawing operation be kicked off? right now I am doing setNeedsDisplay. I could change to do something else.

The view in question is within a UIScrollView but has no other user interaction.

¿Fue útil?

Solución

Here's a framework to start with. The code assumes that the output view is a UIImageView. The background task uses a bitmapped memory context to create a new image for the output view. When the background drawing task completes, the main thread assigns the resulting image to the UIImageView.

The sequenceNumber is used to discard images that are out of date before they finish being drawn. One possible problem is that events arrive so quickly that every image is out of date before being finished. Solving that problem is left as an exercise for the reader.

@interface SomeClass()
@property (weak, nonatomic) IBOutlet UIImageView *someView;
@property (nonatomic) int64_t sequenceNumber;
@end

@implementation SomeClass

- (UIImage *)createImageUsingBitmapMemoryContextOfSize:(CGSize)size
{
    // create the memory bitmap context
    UIGraphicsBeginImageContext( size );
    CGContextRef context = UIGraphicsGetCurrentContext();

    // draw something
    [[UIColor blueColor] set];
    CGContextFillEllipseInRect( context, CGRectMake(0, 0, size.width, size.height) );

    // create a UIImage 
    UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    // return the image
    return( result );
}

// Note: the following function should only be called on the main thread
- (void)updateInResponseToSomeEvent
{
    self.sequenceNumber++;
    int64_t sequenceNumber = self.sequenceNumber;

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^
    {
        UIImage *image = [self createImageUsingBitmapMemoryContextOfSize:self.someView.bounds.size];
        dispatch_async( dispatch_get_main_queue(), ^
        {
            if ( sequenceNumber == self.sequenceNumber )
                self.someView.image = image;
        });
    });
}

Otros consejos

Don't draw on your view in the background as UIKit is not thread safe ! In the background you can draw on to an image, and when drawing is finished, update your view in the main thread.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top