Alex Stone
Alex Stone

Reputation: 47358

A faster way to update interface using background threads

I'm building a spectrograph and would like to know how I can improve the performance of my UIView-based code. I know that I cannot update user interface for iPhone/iPad from a background thread, so I'm doing most of my processing using GCD. The issue that I'm running into is that my interface still updates way too slowly.

With the code below, I'm trying to take 32 stacked 4x4 pixel UIViews and change their background color (see the green squares on the attached image). The operation produces visible lag for other user interface.

Is there a way I can "prepare" these colors from some kind of background thread and then ask the main thread to refresh the interface all at once?

enter image description here

//create a color intensity map used to color pixels
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    colorMap = [[NSMutableDictionary alloc] initWithCapacity:128];

    for(int i = 0; i<128; i ++)
    {
        [colorMap setObject:[UIColor colorWithHue:0.2 saturation:1 brightness:i/128.0 alpha:1] forKey:[NSNumber numberWithInt:i]];
    }


});

-(void)updateLayoutFromMainThread:(id)sender
{
    for(UIView* tempView in self.markerViews)
    {
        tempView.backgroundColor =[colorMap objectForKey:[NSNumber numberWithInt:arc4random()%128]];
    }

}
//called from background, would do heavy processing and fourier transforms
-(void)updateLayout
{

    //update the interface from the main thread
    [self performSelectorOnMainThread:@selector(updateLayoutFromMainThread:) withObject:nil waitUntilDone:NO];


}

I ended up pre-calculating a dictionary of 256 colors and then asking the dictionary for the color based on the value that the circle is trying to display. Trying to allocate colors on the fly was the bottleneck.

Upvotes: 2

Views: 316

Answers (1)

TheBasicMind
TheBasicMind

Reputation: 3585

, Yes, a couple of points.

While you shouldn't process UIView on the main thread, you can instantiate views on a background thread before using them. Not sure if that will help you at all. However beyond instantiating a view on a background thread, UIView's are really just a meta-data wrapper for CALayer objects and are optimised for flexibility rather than performance.

Your best bet is to draw to a layer object or an image object on a background thread (which is a slower process because drawing uses the CPU as well as the GPU), pass the layer object or image to the main thread, then draw the pre-rendered image to your view's layer (much faster because a simple call is made to get the Graphics Processor to blit the image to the UIView's backing store directly).

see this answer:

Render to bitmap then blit to screen

The code:

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextDrawImage(context, rect, image);
}

executes far faster than if you were to execute other drawing operations, such as drawing bezier curves, in the same method.

Upvotes: 1

Related Questions