简体   繁体   中英

Performance Issue while blurring image in UITableViewCell

I got an UIImageView in my custom UITableViewCell . The included image should be blurred. I am aware of UIVisualEffectsView but first of all this is not available prior iOS8 and second the blur is a bit to heavy for my use case.

Thats why I came up with this solution:

Sample cellForRowAtIndexPath:

- (UITableViewCell *)tableView:(UITableView *)tableView
         cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *cellIdentifier = @"showCell";

    DEShowCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];

    if (cell == nil)
    {
        [tableView registerNib:[UINib nibWithNibName:@"DEShowCell" bundle:[NSBundle mainBundle]] forCellReuseIdentifier:cellIdentifier];
        cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
    }

    [cell setBackgroundImageWithBlur:[UIImage imageNamed:@"sampleBanner"]];

    return cell;
}

My custom cell:

-(void)setBackgroundImageWithBlur:(UIImage *)image {
    [self.backgroundImageView setImage:[self blurWithCoreImage:image]];
}

- (UIImage *)blurWithCoreImage:(UIImage *)sourceImage
{
    CIImage *inputImage = [CIImage imageWithCGImage:sourceImage.CGImage];

    // Apply Affine-Clamp filter to stretch the image so that it does not
    // look shrunken when gaussian blur is applied
    CGAffineTransform transform = CGAffineTransformIdentity;
    CIFilter *clampFilter = [CIFilter filterWithName:@"CIAffineClamp"];
    [clampFilter setValue:inputImage forKey:@"inputImage"];
    [clampFilter setValue:[NSValue valueWithBytes:&transform objCType:@encode(CGAffineTransform)] forKey:@"inputTransform"];

    // Apply gaussian blur filter with radius of 30
    CIFilter *gaussianBlurFilter = [CIFilter filterWithName: @"CIGaussianBlur"];
    [gaussianBlurFilter setValue:clampFilter.outputImage forKey: @"inputImage"];
    [gaussianBlurFilter setValue:@10 forKey:@"inputRadius"];

    CIContext *context = [CIContext contextWithOptions:nil];
    CGImageRef cgImage = [context createCGImage:gaussianBlurFilter.outputImage fromRect:[inputImage extent]];

    // Set up output context.
    UIGraphicsBeginImageContext(self.frame.size);
    CGContextRef outputContext = UIGraphicsGetCurrentContext();

    // Invert image coordinates
    CGContextScaleCTM(outputContext, 1.0, -1.0);
    CGContextTranslateCTM(outputContext, 0, -self.frame.size.height);

    // Draw base image.
    CGContextDrawImage(outputContext, self.frame, cgImage);

    // Apply white tint
    CGContextSaveGState(outputContext);
    CGContextSetFillColorWithColor(outputContext, [UIColor colorWithWhite:1 alpha:0.2].CGColor);
    CGContextFillRect(outputContext, self.frame);
    CGContextRestoreGState(outputContext);

    // Output image is ready.
    UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return outputImage;
}

Unfortunately this gives me huge performance issues when I try to scroll the UITableView .

So I asked me how to workaround ? I could use some library to do the blurring like GPUImage which will be faster I guess, but I don't know if this can make a big difference.

The UITableView will contain about 20-60 rows I think.

Any other ideas like caching or something else ?

There's no reason to keep computing the blurred background image. Since everything is using the same one, just blur it once, and assign it when you configure the cell:

if (cell == nil)
{
    [tableView registerNib:[UINib nibWithNibName:@"DEShowCell" bundle:[NSBundle mainBundle]] forCellReuseIdentifier:cellIdentifier];
    cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
    // Inside the initial configuration; not outside.
    [cell.backgroundImageView setImage:self.blurredBackgroundImage];
}

Setup self.blurredBackgroundImage one time:

- (void)viewDidLoad {
    self.blurredBackgroundImage = [self blurWithCoreImage:[UIImage imageNamed:...]];
}

Of course, given this code, I'd probably just blur the image in PhotoShop and save it as the resource. There's no reason to programmatically blur a static resource (unless you're using the same image elsewhere and it's large enough that the disk space matters).

I will use FXBlurView with setting dynamic = NO. It has decent performance.

One of the WWDC 2013 code samples , iOS_UIImageEffects, walks through the use of vImage high performance framework for blurring images. When I tested it, it was considerably faster than CoreImage.

But in answer to your question about caching, yes, absolutely, employ caching. Use NSCache as your primary cache, and persistent memory as a secondary cache.


BTW, you appear to be blurring the same image for every cell. If so, you should blur it once, and then reuse that blurred image for all of the cells.

Likewise, you are registering the NIB multiple times. Register it once up front.

If you do wish to use CoreImage, you should not constantly create the context you're going to use with it. You should create the context once and re-use it, as not doing so can cause this sort of performance issue.

You can figure out whether that's the issue in the above code, or whether it's something else by profiling your code using the time profiler instrument. To do that, just select "Profile" from the "Product" menu in Xcode and when it runs, choose "Time Profiler". You'll then be able to see a stack trace of the calls that took up the most time.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM