I am trying to create a flip transition that contains a grid of CALayer
cells that flip over and 'bounce' after they have done so. However, I am encountering a weird vertical flipping issue shown in this video. Without the 'bounce' animation at the end, the animation works as expected, so it's nothing to do with the image itself, or the splicing of the image into the individual cell layers before animating.
Animation Code (for each individual CALayer
cell)
@interface transitionCell : CALayer
@property (nonatomic) UIImage* startImage;
@property (nonatomic) UIImage* endImage;
@property (nonatomic, readonly) BOOL animationDidFinish;
@property (nonatomic, readonly) BOOL isAnimating;
@property (nonatomic) CGFloat zRotateFactor;
@end
@implementation transitionCell
-(void) setStartImage:(UIImage *)startImage {
self.contents = (__bridge id)startImage.CGImage;
}
-(void) startAnimationWithDuration:(CGFloat)duration { // First half of rotation
_isAnimating = YES;
CABasicAnimation* yRotation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];
yRotation.fromValue = @(0);
yRotation.toValue = @(M_PI*0.5);
CABasicAnimation* zRotation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
zRotation.fromValue = @(0);
zRotation.toValue = @(zRotateMax*_zRotateFactor); // zRotateMax is M_PI*0.2 & _zRotateFactor is a value between -1 and 1.
CAAnimationGroup* rotations = [CAAnimationGroup animation];
rotations.duration = duration*0.5;
rotations.delegate = self;
rotations.removedOnCompletion = NO;
rotations.fillMode = kCAFillModeForwards;
rotations.animations = @[yRotation, zRotation];
[self addAnimation:rotations forKey:@"startRotation"];
}
-(void) animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { // Second half of rotation
if (anim == [self animationForKey:@"startRotation"] && flag) {
self.contents = (__bridge id)_endImage.CGImage;
CABasicAnimation* yRotation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];
yRotation.toValue = @(M_PI);
yRotation.fromValue = @(M_PI*0.5);
CABasicAnimation* zRotation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
zRotation.toValue = @(0);
zRotation.fromValue = @(zRotateMax*_zRotateFactor);
CAAnimationGroup* rotations = [CAAnimationGroup animation];
rotations.duration = anim.duration;
rotations.removedOnCompletion = NO;
rotations.fillMode = kCAFillModeForwards;
rotations.delegate = self;
rotations.animations = @[yRotation, zRotation];
[self addAnimation:rotations forKey:@"endRotate"];
} else if (anim == [self animationForKey:@"endRotate"] && flag) { // The 'Bounce' animation (the one causing the issues)
CABasicAnimation* yRotation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];
yRotation.toValue = @(M_PI+(M_PI*0.2));
yRotation.fromValue = @(M_PI);
CABasicAnimation* zRotation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
zRotation.fromValue = @(0);
zRotation.toValue = @(-zRotateMax*_zRotateFactor*0.2);
CAAnimationGroup* rotations = [CAAnimationGroup animation];
rotations.duration = 0.2;
rotations.removedOnCompletion = NO;
rotations.fillMode = kCAFillModeForwards;
rotations.delegate = self;
rotations.autoreverses = YES;
rotations.animations = @[yRotation, zRotation];
[self addAnimation:rotations forKey:@"endRotate2"];
} else if (anim == [self animationForKey:@"endRotate2"] && flag) {
_animationDidFinish = YES;
}
}
@end
Initialisation Code (In the parent UIViewController
)
-(instancetype) initWithStartImage:(UIImage*)startImage endImage:(UIImage*)endImage {
if (self = [super init]) {
CGFloat baseNodeHeight = screenWidth()/baseNumberNodesWidth;
numNodesHeight = roundf(screenHeight()/baseNodeHeight);
numNodesWidth = roundf(screenWidth()/baseNodeHeight);
moveUpdateFreq = moveBaseUpdateFreq/(numNodesWidth*numNodesHeight);
cellMoveUpdateFreq = cellMoveBaseUpdateFreq/(numNodesWidth*numNodesHeight);
CGFloat const nodeWidth = screenWidth()/numNodesWidth;
CGFloat const nodeHeight = screenHeight()/numNodesHeight;
transition = (transitionType)arc4random_uniform(transitionTypeCount);
cellArray = [NSMutableArray array];
for (int x = 0; x < numNodesWidth; x++) {
[cellArray addObject:[NSMutableArray array]];
for (int y = 0; y < numNodesHeight; y++) {
transitionCell* c = [transitionCell layer];
c.frame = (CGRect){{x*nodeWidth, y*nodeHeight}, {nodeWidth, nodeHeight}};
CGRect startSubRect = {{c.frame.origin.x, screenHeight()-c.frame.origin.y-c.frame.size.height}, c.frame.size};
CGRect endSubRect = {{c.frame.origin.x, c.frame.origin.y}, c.frame.size};
c.startImage = [startImage imageFromSubRect:startSubRect];
c.endImage = [[endImage flippedVerticalImage] imageFromSubRect:endSubRect];
c.zRotateFactor = -(((CGFloat)y-((CGFloat)numNodesHeight*0.5))/(CGFloat)numNodesHeight);
[self.view.layer addSublayer:c];
[cellArray[x] addObject:c];
}
}
}
return self;
}
Any ideas what I'm doing wrong?
Well, I can only assume this is a bug, but nonetheless I have found a quick fix. By adding a xRotation animation set to go from pi to pi on the 'bounce' animation, the problem is fixed. For example, in the bounce animation code: