title: Core Animation CALayer mask animation performance
body: <p>We wanted to use a UITabBar in our iPhone app, but with one exception: we have a "sync" button which I wanted to rotate while the sync operation is happening.</p>
  
  <p><img src="https://i.stack.imgur.com/Hzz2j.png" alt="alt text"></p>
  
  <p>Unfortunately this meant having to create a custom tab bar, but that's neither here nor there: the animation I implemented using Core Animation looks awesome. The problem is that while animating, it adversely affects the performance of everything else using animation on the screen: UITableView scrolling, MKMapView panning and pin drops, etc. My test device is an iPhone 4.</p>
  
  <p>The problem seems to be how I've implemented the tab bar - I wanted to achieve something very similar to UITabBar, where you just supply a PNG for the icon and it uses the alpha channel to create the normal and highlighted states by masking a background image. I accomplished this with CALayer's <code>mask</code> property:</p>
  
  <pre><code>// Inside a UIView subclass' init method...
  
  // Create the mask layer by settings its contents as our PNG icon.
  CALayer *maskLayer = [CALayer layer];
  maskLayer.frame = CGRectMake(0, 0, 31, 31);
  maskLayer.contentsGravity = kCAGravityCenter;
  maskLayer.contentsScale = [[UIScreen mainScreen] scale];
  maskLayer.rasterizationScale = [[UIScreen mainScreen] scale];
  maskLayer.contents = (id)symbolImage.CGImage;
  maskLayer.shouldRasterize = YES;
  maskLayer.opaque = YES;
  
  fgLayer = [[CALayer layer] retain];
  fgLayer.frame = self.layer.frame;
  fgLayer.backgroundColor = [UIColor colorWithImageNamed:@"tabbar-normal-bg.png"].CGColor;
  fgLayer.mask = maskLayer; // Apply the mask
  fgLayer.shouldRasterize = YES;
  fgLayer.opaque = YES;
  
  [self.layer addSublayer:fgLayer];
  </code></pre>
  
  <p>(Note: in the screenshot above you can see I've also added a shadow layer, but for simplicity I removed that from the code. I remove the shadow layer from the sync icon when it is animating, so it shouldn't be relevant.)</p>
  
  <p>To animate, I simply rotate the mask layer:</p>
  
  <pre><code>- (void)startAnimating {
      CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath: @"transform"];
      CATransform3D transform = CATransform3DMakeRotation(RADIANS(179.9), 0.0, 0.0, 1.0);
      animation.toValue = [NSValue valueWithCATransform3D:transform];
      animation.duration = 5;
      animation.repeatCount = 10000;
      animation.removedOnCompletion = YES;
      [fgLayer.mask addAnimation:animation forKey:@"rotate"]; // Add animation to the mask
  }
  </code></pre>
  
  <p>So this all works great except for performance. You can see I've already tried tips that turned up on Google about rasterizing layers / making them opaque - hasn't helped.</p>
  
  <p>I think I've identified the mask layer as being the culprit. When I take out the mask layer and just rotate the <code>fgLayer</code> instead of its mask, performance is wonderful, though it's certainly not the affect I'm going for:</p>
  
  <p><img src="https://i.stack.imgur.com/dsbr4.png" alt="alt text"></p>
  
  <p>Performance is also just as bad as before if I rotate the <code>fgLayer</code> instead of the mask <em>while</em> the mask is applied.</p>
  
  <p>So if having to recomposite the mask each frame of the animation is the slow down, are there any other techniques I can use to achieve a similar affect that will have better performance? Using a path instead of an image for the mask layer? Or am I going to have to drop down to OpenGL or something to get good performance?</p>
  
  <p><strong>UPDATE:</strong> further reinforcing the idea that the mask is the slowdown, my coworker suggested trying to rotate a CALayer with just the image as the contents -- so similar to my example above w/o a mask -- and performance was also good that way. So I can really only do a solid color like that (no gradient), but it may be a good interim solution. I'd still love to achieve rotating a mask with good performance though, so suggestions welcome :)</p>