Search code examples
cocoacore-animationcalayer

How to animate a png sequence with core animation in cocoa (not touch)


I would like to animate a png sequence in a NSImageView, but I cannot make it work. It just does not want to show any animation. Any suggestion?

This is my code:

- (void) imageAnimation {
  NSMutableArray *iconImages = [[NSMutableArray alloc] init];
  for (int i=0; i<=159; i++) {
    NSString *imagePath = [NSString stringWithFormat:@"%@_%05d",@"clear",i];
    [iconImages addObject:(id)[NSImage imageNamed:imagePath]];
    //NSImage *iconImage = [NSImage imageNamed:imagePath];
    //[iconImages addObject:(__bridge id)CGImageCreateWithNSImage(iconImage)];
  }


  CALayer *layer = [CALayer layer];
  CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"contents"];
  [animation setCalculationMode:kCAAnimationDiscrete];
  [animation setDuration:10.0f];
  [animation setRepeatCount:HUGE_VALF];
  [animation setValues:iconImages];

  [layer setFrame:NSMakeRect(0, 0, 104, 104)];
  layer.bounds = NSMakeRect(0, 0, 104, 104);
  [layer addAnimation:animation forKey:@"contents"];

  //Add to the NSImageView layer
  [iconV.layer addSublayer:layer];
}

Solution

  • tl;dr:

    Make your view layer-hosting by calling

    [iconV setLayer:[CALayer layer]];
    [iconV setWantsLayer:YES];
    

    Why nothing happens

    The reason nothing is happening is that your image view doesn't have a layer so when you call [iconV.layer addSublayer:layer]; you are sending a message to nil and nothing happens (the sublayer is not added to the image view).

    Views on OS X don't use Core Animation layers as their backing store by default for backwards compatibility. A view with a layer can either be layer-backed or layer-hosting.

    You should not interact directly with a layer-backed view and you shouldn't add views (but adding layers is ok) to a layer-hosting view. Since you are adding the layer to your image views layer (and thus interacting directly with it) you want a layer-hosting view.

    Fixing it

    You can tell your view that it should be layer-hosting by first giving it the layer using [iconV setLayer:[CALayer layer]]; and then (order is important) telling it that it wants a layer using [iconV setWantsLayer:YES];.

    For more information on layer-backed and layer-hosting views please read the documentation for wantsLayer.