6

Using CAEmitterLayer, @2x (retina) images aren't being scaled property like they do elsewhere in iOS. The result I'm getting is the @2x version is being displayed 4x the size of the non-retina image instead of being scaled down.

Any idea of how to fix this? I have tried testing the image pain in a UIImageView and the results are as they should be, so this appears to be a problem with CAEmitterLayer and CAEmitterCell. The images have the correct @2x.png specifier.

Here's the code I'm using :

CAEmitterLayer *fallingCoinEmitter = [CAEmitterLayer layer];
fallingCoinEmitter.emitterPosition = CGPointMake(self.view.bounds.size.width / 2.0, -30);
fallingCoinEmitter.emitterSize = CGSizeMake(self.view.bounds.size.width * 2.0, 0.0);;

    // Spawn points for the flakes are within on the outline of the line
fallingCoinEmitter.emitterMode  = kCAEmitterLayerOutline;
fallingCoinEmitter.emitterShape = kCAEmitterLayerLine;

    // Configure the snowflake emitter cell
CAEmitterCell *coin = [CAEmitterCell emitterCell];
coin.birthRate      = 8.0;
coin.lifetime       = 5.0;
coin.velocity       = -180;             // falling down slowly
coin.velocityRange = 80;
coin.yAcceleration = 40;
coin.emissionRange = 0.4 * M_PI;        // some variation in angle
coin.spinRange      = 0.45 * M_PI;      // slow spin
coin.contents       = (id) [[UIImage imageNamed:@"Coin_Generic_Emitter"] CGImage];
coin.scale          = 1.0;
coin.scaleRange     = 0.0;

    // Make the flakes seem inset in the background
fallingCoinEmitter.shadowOpacity = 1.0;
fallingCoinEmitter.shadowRadius  = 4.0;
fallingCoinEmitter.shadowOffset  = CGSizeMake(0.0, 3.0);
UIColor *darkGreenColor = [UIColor colorWithRed:0.005 green:0.163 blue:0.005 alpha:1.000];
fallingCoinEmitter.shadowColor   = [darkGreenColor CGColor];
[fallingCoinEmitter setContentsScale:[UIScreen mainScreen].scale];
//fallingCoinEmitter.shouldRasterize = YES;
//[fallingCoinEmitter setRasterizationScale:[UIScreen mainScreen].scale];
//fallingCoinEmitter.scale = fallingCoinEmitter.scale / [[UIScreen mainScreen] scale];

    // Add everything to our backing layer below the UIContol defined in the storyboard
fallingCoinEmitter.emitterCells = [NSArray arrayWithObject:coin];
[self.view.layer insertSublayer:fallingCoinEmitter atIndex:0];

Thanks!

Update:

@Fabian, setting contentScale isn't working, at least not my solution

    [fallingCoinEmitter setContentsScale:[UIScreen mainScreen].scale];

I also tried this with no results..

    emitter.shouldRasterize = YES;
    [emitter setRasterizationScale:[UIScreen mainScreen].scale];

And settings the scale range did not work. There's still a difference between the sizes on the iPad 2 and 3 (w RD).

W Dyson
  • 4,604
  • 4
  • 40
  • 68

1 Answers1

8

You should try modifying your CAEmmitterCells' scale and scaleRange properties depending on the device's screen.

cell.scale = cell.scale / [[UIScreen mainScreen] scale];

Fabian Kreiser
  • 8,307
  • 1
  • 34
  • 60
  • I'll try that. I'd like to know though, if there is a 'proper' solution to this instead of hacking it every time. – W Dyson Feb 05 '13 at 18:06
  • Well, this *is* a proper solution. Core Animation doesn't know or care about the screen's point to pixel scale. `contentsScale` just makes sure that the layer's content (the image) is scaled properly. As you're using `CAEmmitterLayer`, you'll have to scale the emmitted cells yourself, because the emmitter cells don't care about whether their content should be scaled or not. – Fabian Kreiser Feb 05 '13 at 18:11
  • Didn't mean to degrate your answer. I just can't believe Apple would add retina support for things like UIImageView and leave others. – W Dyson Feb 05 '13 at 18:14
  • As I said, Core Animation doesn't know anything about the scaling. Read the documentation about the `contentsScale` property: https://developer.apple.com/library/mac/#documentation/graphicsimaging/reference/CALayer_class/Introduction/Introduction.html – Fabian Kreiser Feb 05 '13 at 18:28
  • The documentation says that for layers that are attached to a view, the `contentsScale` will automatically be set, but for layers that you create you are responsible for setting it. `CAEmmitterLayer` is an exception where this doesn't work for images in the emmitted cells, because your @2x image is part of `CAEmmitterCell`, an abstract object used by `CAEmmitterLayer` to render the particles. I hope you understand what I'm saying. Did setting the `scale` work, btw? – Fabian Kreiser Feb 05 '13 at 18:30
  • The `contents` of `CAEmmitterCell` isn't equal to the `contents` of the `CAEmmitterLayer` but only used by the latter to render the bitmap and because of that you have to set the `scale` manually in order to get the desired behavior on devices with a retina display. – Fabian Kreiser Feb 05 '13 at 18:32