How to animate a png sequence with core animation

2019-06-25 03:32发布

问题:

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];
}

回答1:

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.



回答2:

I am late to the party but I will post my solution because the original one plus the answer posted by @David did not work for me. I am creating on Xcode 8.3.3, compiling for 10.10.

What did not work for me was to create the layer separately and then set the image view with it.

this did not work for me

CALayer *layer = [CALayer layer];
layer.frame = self.imageView.bounds;
layer.bounds = self.imageView.bounds;
[self.imageView setLayer:layer];

The result was always no animation.

Apparently, on 10.10, when you do this:

self.imageView.wantsLayer = YES;

MacOS creates a layer and assigns it to the image view.

So, the code that worked for me was:

self.imageView.wantsLayer = YES;

CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"contents"];
[animation setCalculationMode:kCAAnimationDiscrete];
[animation setDuration:1.3f];
[animation setRepeatCount:HUGE_VALF];
[animation setValues:imageArray];
[self.imageView.layer addAnimation:animation forKey:@"contents"];  
[self.imageView setAnimates:YES];

[self.imageView setCanDrawSubviewsIntoLayer:YES];

This last line is very important. If you remove that it will not show any image!