I have been running into some issues with animating multiple CALayers at the same time, and was hoping someone could point me in the right direction.
My app contains an array of CALayer. The position of each layer is set to (previousLayer.position.y + previousLayer.bounds.height)
, which basically lays them out si开发者_如何学运维milar to a table. I then have a method that, every-time it is called, adds a new layer to the stack and sets its Y position is set to 0. The Y positions of all other layers in the array are then offset by the height of the new layer (essentially pushing all old layers down).
What I am having problems with is preventing the adding of new layers until the previous animation has completed. Is there a way to tell when an implicit animation has finished? Or alternatively, if I use CABasicAnimation
and animationDidFinish
, is there a way to tell which object finished animating when animationDidFinish
is called?
You can set arbitrary values for keys on your animation object. What this means is that you can associate your layer that you're animating with the animation and then query it in -animationDidStop:finished: You create your animation this way:
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"position"];
// set other fields...
[anim setValue:layerToAnimate forKey:@"layer"];
// Start the animation
[layerToAnimate addAnimation:anim forKey:nil];
Then check for that value when the animation stops:
- (void)animationDidStop:(CAAnimation *)animation finished:(BOOL)flag
{
CALayer *animatedLayer = [animation valueForKey:@"layer"];
// do something with the layer based on some condition...
// spin off the next animation...
[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue
forKey:kCATransactionDisableActions];
[animatedLayer setPosition:position];
[CATransaction commit];
}
This is explicit animation, but it should give you what you need.
You can try surrounding your code in a CATransaction
. Here's how that would look in Swift 3:
CATransaction.begin()
CATransaction.setCompletionBlock({
// run after the animations
})
// animtations
CATransaction.commit()
It turns out that rather than adding the CABasicAnimation directly to the CALayer, I had to add it to the layer's 'actions' dictionary... This leaves the layer at it's final position after the animation ends, but still calls the 'animationDidFinish' method.
-(void)startAnimation {
if(!animating){
animating = YES;
for(int i=0; i<[tweets count]; i++) {
//get the layer
CETweetLayer *currentLayer = [tweets objectAtIndex:i];
//setup the orgin and target y coordinates
float targetY = currentLayer.position.y + offset;
//setup the animation
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"position"];
anim.delegate = self;
currentLayer.actions = [NSDictionary dictionaryWithObject:anim forKey:@"position"];
currentLayer.position = CGPointMake(self.bounds.size.width/2, targetY);
}
}
}
And then...
-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
animating = NO;
}
精彩评论