Reputation: 33
I'm trying to do some animations using Core Animation on the iphone. I'm using CABasicAnimation on CALayer. It's a straight forward animation from a random place at the top of the screen to the bottom of the screen at random speed, I have 30 elements that doing the same animation continuously until another action happens. But the performance on the iPhone 3G is very sluggish when the animations start. The image is only 8k.
Is this the right approach? How should I change so it performs better.
// image cached somewhere else.
CGImageRef imageRef = [[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:name ofType:@"png"]] CGImage];
- (void)animate:(NSTimer *)timer
{
int startX = round(radom() % 320);
float speed = 1 / round(random() % 100 + 2);
CALayer *layer = [CALayer layer];
layer.name = @"layer";
layer.contents = imageRef; // cached image
layer.frame = CGRectMake(0, 0, CGImageGetWidth(imageRef), CGImageGetHeight(imageRef));
int width = layer.frame.size.width;
int height = layer.frame.size.height;
layer.frame = CGRectMake(startX, self.view.frame.origin.y, width, height);
[effectLayer addSublayer:layer];
CGPoint start = CGPointMake(startX, 0);
CGPoint end = CGPointMake(startX, self.view.frame.size.height);
float repeatCount = 1e100;
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
animation.delegate = self;
animation.fromValue = [NSValue valueWithCGPoint:start];
animation.toValue = [NSValue valueWithCGPoint:end];
animation.duration = speed;
animation.repeatCount = repeatCount;
animation.autoreverses = NO;
animation.removedOnCompletion = YES;
animation.fillMode = kCAFillModeForwards;
[layer addAnimation:animation forKey:@"position"];
}
The animations are fired off using a NSTimer.
animationTimer = [NSTimer timerWithTimeInterval:0.2 target:self selector:@selector(animate:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:animationTimer forMode:NSDefaultRunLoopMode];
Upvotes: 0
Views: 1907
Reputation: 1193
In my experience layer.contents = imageRef; // cached image is the most expensive call in this method.
For testing purposes I would use the same image object on them all. The first might be sluggish but the rest will be smooth.
As far as I understand, the reason for this is because an UIImage object even when created does actually pull all the data from the file until it is going to be drawn on screen.
There are several posts out there that show that using nsurlconnections to download the images with the file:// protocol will actually thread the file getting (without writing any threading code) and because you are animated layers and not uiviews, it really will be asynchronous.
Upvotes: 1
Reputation: 43452
Okay, a couple of things here:
Why are you using an NSTimer to repeatedly fire the animation? Just have the animations repeat. If they need pause and resync you can do that with a keyed animation. By programming it all up you can let the renderer run without having to frequently synch in new information into the render tree.
You are doing:
CGImageRef imageRef = [[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:name ofType:@"png"]] CGImage];
for 30 objects each on a timer that fires every 0.2 seconds? Have you profiled your app? What percentage of the time is spent in image decoding? If you switch over to using imageNamed: iPhone OS will cache images so long as there is enough ram, which should reduce your decode time:
CGImageRef imageRef = [[UIImage imageNamed:[NSString stringWithFormat:@"%@.png", name]] CGImage];
Also, do your images have transparency that needs to be alpha composited? If you can avoid alpha it is a huge speed win on the older iPhones.
Upvotes: 3