title: How to render a CALayer in the background
body: <p>I need to save screenshots from my app, so I've set up code like this, which works:</p>
  
  <pre><code>- (void)renderScreen {
      UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];
  
      CGSize outputSize = keyWindow.bounds.size;
      UIGraphicsBeginImageContext(outputSize);
      CGContextRef context = UIGraphicsGetCurrentContext();
  
      CGContextSaveGState(context);
      CALayer *layer = [keyWindow layer];
      [layer renderInContext:context];
      CGContextRestoreGState(context);
  
      UIImage *screenImage = UIGraphicsGetImageFromCurrentImageContext();
      UIGraphicsEndImageContext();
  
      // now save the screen image, etc...
  }
  </code></pre>
  
  <p>However, when the screen image becomes complex (lots of views), the renderInContext can take up to 0.8 seconds on an iPad 3, and the user interface locks up during that time, which interferes with some other functionality. So I moved the rendering to a background thread, like this:</p>
  
  <pre><code>- (void)renderScreen {
      UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];
      CALayer *layer = [keyWindow layer];
      [self performSelectorInBackground:@selector(renderLayer:) withObject:layer];
  }
  
  - (void)renderLayer:(CALayer *)layer {
      UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];
  
      CGSize outputSize = keyWindow.bounds.size;
      UIGraphicsBeginImageContext(outputSize);
      CGContextRef context = UIGraphicsGetCurrentContext();
  
      CGContextSaveGState(context);
      [layer renderInContext:context];
      CGContextRestoreGState(context);
  
      UIImage *screenImage = UIGraphicsGetImageFromCurrentImageContext();
      UIGraphicsEndImageContext();
  
      // now save the screen image, etc...
  }
  </code></pre>
  
  <p>That allows the interface to run smoothly again, but occasionally causes a crash with EXC_BAD_ACCESS on the renderInContext line. I tried checking for layer!=nil and [layer respondsToSelector:@selector(renderInContext:)] first, so I could avoid the crash, but both conditions always return true.</p>
  
  <p>Then I read <a href="https://stackoverflow.com/questions/11253972/ipad-3-device-crash-with-renderincontext#comment14791808_11254049">this SO comment</a>, stating that a layer could mutate before the background operation runs and suggesting sending a copy of the layer to the background operation instead. <a href="https://stackoverflow.com/a/695540/462162">This SO answer</a> and <a href="https://stackoverflow.com/a/962119/462162">this one</a> got me started, and I ended up with this category to add a copy method to CALayer:</p>
  
  <pre><code>#import "QuartzCore/CALayer.h"
  
  @interface CALayer (CALayerCopyable)
  - (id)copy;
  @end
  
  @implementation CALayer (CALayerCopyable)
  
  - (id)copy {
      CALayer *newLayer = [CALayer layer];
      newLayer.actions = [self.actions copy];
      newLayer.anchorPoint = self.anchorPoint;
      newLayer.anchorPointZ = self.anchorPointZ;
      newLayer.backgroundColor = self.backgroundColor;
      //newLayer.backgroundFilters = [self.backgroundFilters copy]; // iOS 5+
      newLayer.borderColor = self.borderColor;
      newLayer.borderWidth = self.borderWidth;
      newLayer.bounds = self.bounds;
      //newLayer.compositingFilter = self.compositingFilter; // iOS 5+
      newLayer.contents = [self.contents copy];
      newLayer.contentsCenter = self.contentsCenter;
      newLayer.contentsGravity = [self.contentsGravity copy];
      newLayer.contentsRect = self.contentsRect;
      //newLayer.contentsScale = self.contentsScale; // iOS 4+
      newLayer.cornerRadius = self.cornerRadius;
      newLayer.delegate = self.delegate;
      newLayer.doubleSided = self.doubleSided;
      newLayer.edgeAntialiasingMask = self.edgeAntialiasingMask;
      //newLayer.filters = [self.filters copy]; // iOS 5+
      newLayer.frame = self.frame;
      newLayer.geometryFlipped = self.geometryFlipped;
      newLayer.hidden = self.hidden;
      newLayer.magnificationFilter = [self.magnificationFilter copy];
      newLayer.mask = [self.mask copy]; // property is another CALayer
      newLayer.masksToBounds = self.masksToBounds;
      newLayer.minificationFilter = [self.minificationFilter copy];
      newLayer.minificationFilterBias = self.minificationFilterBias;
      newLayer.name = [self.name copy];
      newLayer.needsDisplayOnBoundsChange = self.needsDisplayOnBoundsChange;
      newLayer.opacity = self.opacity;
      newLayer.opaque = self.opaque;
      newLayer.position = self.position;
      newLayer.rasterizationScale = self.rasterizationScale;
      newLayer.shadowColor = self.shadowColor;
      newLayer.shadowOffset = self.shadowOffset;
      newLayer.shadowOpacity = self.shadowOpacity;
      newLayer.shadowPath = self.shadowPath;
      newLayer.shadowRadius = self.shadowRadius;
      newLayer.shouldRasterize = self.shouldRasterize;
      newLayer.style = [self.style copy];
      //newLayer.sublayers = [self.sublayers copy]; // this line makes the screen go blank
      newLayer.sublayerTransform = self.sublayerTransform;
      //newLayer.superlayer = self.superlayer; // read-only
      newLayer.transform = self.transform;
      //newLayer.visibleRect = self.visibleRect; // read-only
      newLayer.zPosition = self.zPosition;
      return newLayer;
  }
  
  @end
  </code></pre>
  
  <p>Then I updated renderScreen to send a copy of the layer to renderLayer:</p>
  
  <pre><code>- (void)renderScreen {
      UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];
      CALayer *layer = [keyWindow layer];
      CALayer *layerCopy = [layer copy];
      [self performSelectorInBackground:@selector(renderLayer:) withObject:layerCopy];
  }
  </code></pre>
  
  <p>When I run this code, all the screen images are plain white. Obviously my copy method is not correct. So can someone help me with any of the following possible solutions?</p>
  
  <ol>
  <li>How to write a copy method for CALayer that really works?</li>
  <li>How to check that a layer passed into a background process is a valid target for renderInContext?</li>
  <li>Any other way to render complex layers without locking up the interface?</li>
  </ol>
  
  <p>UPDATE: I rewrote my CALayerCopyable category based on Rob Napier's suggestion to use initWithLayer. Simply copying the layer still gave me a plain white output, so I added a method to recursively copy all the sublayers. I still, however, get the plain white output:</p>
  
  <pre><code>#import "QuartzCore/CALayer.h"
  
  @interface CALayer (CALayerCopyable)
  - (id)copy;
  - (NSArray *)copySublayers:(NSArray *)sublayers;
  @end
  
  @implementation CALayer (CALayerCopyable)
  
  - (id)copy {
      CALayer *newLayer = [[CALayer alloc] initWithLayer:self];
      newLayer.sublayers = [self copySublayers:self.sublayers];
      return newLayer;
  }
  
  - (NSArray *)copySublayers:(NSArray *)sublayers {
      NSMutableArray *newSublayers = [NSMutableArray arrayWithCapacity:[sublayers count]];
      for (CALayer *sublayer in sublayers) {
          [newSublayers addObject:[sublayer copy]];
      }
      return [NSArray arrayWithArray:newSublayers];
  }
  
  @end
  </code></pre>