title: Fast blurring for UITableViewCell contentView Background
body: <p>I have made a <code>UIViewController</code> which conforms to the <code>UITableViewDataSource</code> and <code>UITableViewDelegate</code> protocol and has a <code>UITableView</code> as it's subview.</p>
  
  <p>I have set the <code>backgroundView</code> property of the table to be a <code>UIImageView</code> in order to display an image as the background of the table.</p>
  
  <p>In order to have custom spacings between the cells I made the row height larger than I wanted and customised the cell's contentView to be the size I wanted, making it look like there is extra space (<a href="https://stackoverflow.com/questions/6216839/how-to-add-spacing-between-uitableviewcell">Following this SO answer</a>).</p>
  
  <p>I wanted to add a blur to the cell so that the background was blurred and I did this through <a href="https://github.com/BradLarson/GPUImage" rel="nofollow noreferrer">Brad Larson's GPUImage framework</a>. This works fine however, since I want the background blur to update as it scrolls, the scroll becomes very laggy.</p>
  
  <p>My code is:</p>
  
  <pre><code>//Gets called from the -scrollViewDidScroll:(UIScrollView *)scrollView method
  - (void)updateViewBG
  {
      UIImage *superviewImage = [self snapshotOfSuperview:self.tableView];
  
      UIImage* newBG = [self applyTint:self.tintColour image:[filter imageByFilteringImage:superviewImage]];
  
      self.layer.contents = (id)newBG.CGImage;
      self.layer.contentsScale = newBG.scale;
  }
  
  //Code to create an image from the area behind the 'blurred cell'
  - (UIImage *)snapshotOfSuperview:(UIView *)superview
  {
      CGFloat scale = 0.5;
      if (([UIScreen mainScreen].scale &gt; 1 || self.contentMode == UIViewContentModeScaleAspectFill)) {
          CGFloat blockSize = 12.0f/5;
          scale = blockSize/MAX(blockSize * 2, floor(self.blurRadius));
      }
  
      UIGraphicsBeginImageContextWithOptions(self.bounds.size, YES, scale);
      CGContextRef context = UIGraphicsGetCurrentContext();
      CGContextTranslateCTM(context, -self.frame.origin.x, -self.frame.origin.y);
      NSArray *hiddenViews = [self prepareSuperviewForSnapshot:superview];
      [superview.layer renderInContext:context];
      [self restoreSuperviewAfterSnapshot:hiddenViews];
      UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext();
      UIGraphicsEndImageContext();
      return snapshot;
  }
  
  -(UIImage*)applyTint:(UIColor*)colour image:(UIImage*)inImage{
      UIImage *newImage;
      if (colour) {
          UIGraphicsBeginImageContext(inImage.size);
  
          CGContextRef ctx = UIGraphicsGetCurrentContext();
          CGRect area = CGRectMake(0, 0, inImage.size.width, inImage.size.height);
  
          CGContextScaleCTM(ctx, 1, -1);
          CGContextTranslateCTM(ctx, 0, -area.size.height);
          CGContextSaveGState(ctx);
          CGContextClipToMask(ctx, area, inImage.CGImage);
          [[colour colorWithAlphaComponent:0.8] set];
          CGContextFillRect(ctx, area);
          CGContextRestoreGState(ctx);
          CGContextSetBlendMode(ctx, kCGBlendModeLighten);
          CGContextDrawImage(ctx, area, inImage.CGImage);
          newImage = UIGraphicsGetImageFromCurrentImageContext();
          UIGraphicsEndImageContext();
      } else {
          newImage = inImage;
      }
      return newImage;
  }
  </code></pre>
  
  <p>Now for the question:<br>
  Is there a better way to add the blur? Maybe so that the layer doesn't have to be rendered each movement? iOS7's control centre/notification centre seem to be able to do this without any lagging.</p>
  
  <p>Maybe with the <code>GPUImageUIElement</code> class? If so, how do I use this? 
  Another way I looked at was to create the blur on the background image initially and then crop just the areas I needed to use out, however I couldn't get this to work, since the images may or may not be the same size as the screen so the scaling was a problem (Using <code>CGImageCreateWithImageInRect()</code> and the rect being the cell's position on the table).</p>
  
  <p>I also found out that I have to add the blur to the tableview itself with the frame being that of the cell, and the cell having a clear colour.</p>
  
  <p>Thanks in advance</p>
  
  <p><strong>EDIT</strong>
  Upon request, here is the code for the image cropping I attempted before:</p>
  
  <pre><code>- (void)updateViewBG
  {
      //self.bgImg is the pre-blurred image, -getContentViewFromCellFrame: is a convenience method to get just the content area from the whole cell (since the contentarea is smaller than the cell)
      UIImage* bg = [self cropImage:self.bgImg 
                             toRect:[LATableBlur getContentViewFromCellFrame:[self.tableView rectForRowAtIndexPath:self.cellIndexPath]]];
      bg = [self applyTint:self.tintColour image:bg];
      self.layer.contents = (id)bg.CGImage;
      self.layer.contentsScale = bg.scale;
  }
  
  - (UIImage*)cropImage:(UIImage*)image toRect:(CGRect)frame
  {
      CGSize imgSize = [image size];
      double heightRatio = imgSize.height/self.tableView.frame.size.height;
      double widthRatio = imgSize.width/self.tableView.frame.size.width;
  
      UIImage* cropped = [UIImage imageWithCGImage:CGImageCreateWithImageInRect(image.CGImage,
                                                                                CGRectMake(frame.origin.x*widthRatio,
                                                                                           frame.origin.y*heightRatio,
                                                                                           frame.size.width*widthRatio,
                                                                                           frame.size.height*heightRatio))];
  
      return cropped;
  }
  </code></pre>