Why is my CATransaction not honoring the duration I set?
I'm porting an iPhone app to Mac OS X. This code was being used successfully on the iPhone:
- (void) moveTiles:(NSArray*)tilesToMove {
[UIView beginAnimations:@"tileMovement" context:nil];
[UIView setAnimationDuration:0.1];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(tilesStoppedMoving:finished:context:)];
for( NSNumber* aNumber in tilesToMove ) {
int tileNumber = [aNumber intValue];
UIView* aView = [self viewWithTag:tileNumber];
aView.frame = [self makeRectForTile:tileNumber];
}
[UIView commitAnimations];
}
The Mac version uses CATransaction to group the animations, like so:
- (void) moveTiles:(NSArray*)tilesToMove {
[CATransaction begin];
[CATransaction setAnimationDuration:0.1];
[CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
[CATransaction setCompletionBlock:^{
[gameDelegate tilesMoved];
}];
for( NSNumber* aNumber in tilesToMove ) {
int tileNumber = [aNumber intValue];
NSView* aView = [self viewWithTag:tileNumber];
[[aView animator] setFrame:[self makeRectForTile:tileNumber]];
}
[CATransaction commit];
}
The animation is executing fine, except that the duration is 1.0 seconds. I can change the setAnimationDuration: call to anything,开发者_如何学运维 or omit it completely, and still the animation is 1.0 seconds in duration, every time. I also don't think the setAnimationTimingFunction: call is doing anything. However, setCompletionBlock: is working, because that block is executing when the animation completes.
What am I doing wrong here?
If I am not mistaken you cannot use CoreAnimation to animate NSView's directly. For that you need NSAnimationContext and [NSView animator]. CATransaction will only work with CALayers.
It doesn't answer the question exactly, but I ended up using NSAnimationContext instead of CATransaction.
- (void) moveTiles:(NSArray*)tilesToMove {
[NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext] setDuration:0.1f];
for( NSNumber* aNumber in tilesToMove ) {
int tileNumber = [aNumber intValue];
NSView* aView = [self viewWithTag:tileNumber];
[[aView animator] setFrame:[self makeRectForTile:tileNumber]];
CAAnimation *animation = [aView animationForKey:@"frameOrigin"];
animation.delegate = self;
}
[NSAnimationContext endGrouping];
}
It's works, but I'm not terribly happy about it. Mainly, NSAnimationContext doesn't have a callback completion mechanism like CATransaction does, so I had to put the thing there to explicitly get the view's animation and set the delegate so a callback gets triggered. Problem with that is, it gets triggered multiple times for each animation. This turns out to have no ill effects for what I'm doing, it just feels wrong.
This is workable, but if anyone knows a better solution, I'd still like one.
精彩评论