Search code examples
iphoneiosios5caanimationcatransform3d

3D Animation - CABasicAnimation and CATransform3D giving gap between views


So,I am trying to implement an animation which is in this app http://itunes.apple.com/us/app/spg-mobile-app/id312306003?mt=8 which is in the first view

I have implemented animation for flipping views and i am succeeded in animating one side and when i have given the reverse logic for reverse flipping, it is giving some gap between views

here is my code

- (UIImage *)screenShot: (UIView *) aView
{
    // Arbitrarily masks to 40%. Use whatever level you like
    UIGraphicsBeginImageContext(hostView.frame.size);
    [aView.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    CGContextSetRGBFillColor (UIGraphicsGetCurrentContext(), 0, 0, 0, 0.4f);
    CGContextFillRect (UIGraphicsGetCurrentContext(), hostView.frame);
    UIGraphicsEndImageContext();
    return image;
}


- (CALayer *) createLayerFromView: (UIView *) aView transform: (CATransform3D) transform
{

    CALayer *imageLayer = [CALayer layer];
    imageLayer.anchorPoint = CGPointMake(1.0f, 1.0f);
    imageLayer.frame = (CGRect){.size = hostView.frame.size};
    imageLayer.transform = transform;
    UIImage *shot = [self screenShot:aView];
    imageLayer.contents = (__bridge id) shot.CGImage;

    return imageLayer;
}

- (void)animationDidStart:(CAAnimation *)animation
{

    UIView *source = (UIView *) _source;
    [source removeFromSuperview];
}

- (void)animationDidStop:(CAAnimation *)animation finished:(BOOL)finished
{

    UITableView *dest = (UITableView *) destination;
    dest.frame = CGRectMake(160, 0, 160, 460);
    [hostView addSubview:dest];
    [transformationLayer removeFromSuperlayer];


    //if (delegate)
    //SAFE_PERFORM_WITH_ARG(delegate, @selector(segueDidComplete), nil);
}

-(void)animateWithDuration: (CGFloat) aDuration
{
    goesForward=FALSE ;

    CAAnimationGroup *group = [CAAnimationGroup animation];
    group.delegate = self;
    group.duration = aDuration;

    CGFloat halfWidth = hostView.frame.size.width; // 2.0f;
    float multiplier = goesForward ? -1.0f : 1.0f;

    CABasicAnimation *translationX = [CABasicAnimation animationWithKeyPath:@"sublayerTransform.translation.x"];
    translationX.toValue = [NSNumber numberWithFloat:multiplier * halfWidth];

    CABasicAnimation *translationZ = [CABasicAnimation animationWithKeyPath:@"sublayerTransform.translation.z"];
    translationZ.toValue = [NSNumber numberWithFloat:-200.0]; //halfWidth];

    CABasicAnimation *rotationY = [CABasicAnimation animationWithKeyPath:@"sublayerTransform.rotation.y"];
    rotationY.toValue = [NSNumber numberWithFloat: multiplier * M_PI_2];

    group.animations = [NSArray arrayWithObjects: rotationY, translationX, translationZ, nil];
    group.fillMode = kCAFillModeForwards;
    group.removedOnCompletion = NO;

    [CATransaction flush];
    [transformationLayer addAnimation:group forKey:kAnimationKey];
}

- (void) constructRotationLayer
{

    UIView *source = (UIView *) _source;
    UIView *dest = (UIView *) destination;
    hostView = [source superview];

    //    if ([hostView isKindOfClass:[UIWindow class]]) {
    //        NSLog(@"I am window class");
    //    }

    transformationLayer = [CALayer layer];
    transformationLayer.frame = hostView.bounds;  //CGRectMake(40, 0, 280, 460);    
    transformationLayer.anchorPoint = CGPointMake(0.5f, 0.5f);
    CATransform3D sublayerTransform = CATransform3DIdentity;
    sublayerTransform.m34 = 1.0 / -1000;
    [transformationLayer setSublayerTransform:sublayerTransform];
    [hostView.layer addSublayer:transformationLayer];


    CATransform3D transform = CATransform3DMakeTranslation(0, 0, 0);
    [transformationLayer addSublayer:[self createLayerFromView:source transform:CATransform3DRotate(transform, M_PI_2, 0, 0, 0)]];

    transform = CATransform3DRotate(transform, M_PI_2, 0, 1, 0);
    transform = CATransform3DTranslate(transform, hostView.frame.size.width, 0, 0);

    if (!goesForward)
    {
        transform = CATransform3DRotate(transform, M_PI_2, 0, 1, 0);
        transform = CATransform3DTranslate(transform, hostView.frame.size.width, 0, 0);
        transform = CATransform3DRotate(transform, M_PI_2, 0, 1, 0);
        transform = CATransform3DTranslate(transform, hostView.frame.size.width, 0, 0);
    }

    [transformationLayer addSublayer:[self createLayerFromView:dest transform:transform]];
}

- (void)perform
{
    [self constructRotationLayer];
    [self animateWithDuration:0.4f];
}

So, here i am calling perform method first and Here i have uploaded my total code Here


This Code worked for me a bit but still getting some gap, the thing is the frame of the going view and coming view should be same

- (UIImage *)screenShot: (UIView *) aView
{
    // Arbitrarily masks to 40%. Use whatever level you like
    UIGraphicsBeginImageContext(hostView.frame.size);
    [aView.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    CGContextSetRGBFillColor (UIGraphicsGetCurrentContext(), 0, 0, 0, 0.4f);
    CGContextFillRect (UIGraphicsGetCurrentContext(), hostView.frame);
    UIGraphicsEndImageContext();
    return image;
}


- (CALayer *) createLayerFromView: (UIView *) aView transform: (CATransform3D) transform
{
    CALayer *imageLayer = [CALayer layer];
    imageLayer.anchorPoint = CGPointMake(1.0f, 1.0f);
    imageLayer.frame = (CGRect){.size = hostView.frame.size};
    imageLayer.transform = transform;  
    UIImage *shot = [self screenShot:aView];
    imageLayer.contents = (__bridge id) shot.CGImage;
    return imageLayer;
}

- (void)animationDidStart:(CAAnimation *)animation 
{
//    UIView *source = (UIView *) super.sourceViewController;
    [source removeFromSuperview];
}

- (void)animationDidStop:(CAAnimation *)animation finished:(BOOL)finished 
{
//    UIView *dest = (UIView *) super.destinationViewController;
    if (hostView !=nil) {
        NSLog(@"hostView %@",hostView);
        if (dest) {
    [hostView addSubview:dest];              
        }
    }

    [transformationLayer removeFromSuperlayer];
    if (delegate)
        SAFE_PERFORM_WITH_ARG(delegate, @selector(segueDidComplete), nil);

}

-(void)animateWithDuration: (CGFloat) aDuration
{
    CAAnimationGroup *group = [CAAnimationGroup animation]; 
    group.delegate = self; 
    group.duration = aDuration; 

    CGFloat halfWidth = hostView.frame.size.width / 2.0f;
    float multiplier = goesForward ? -1.0f : 1.0f;

    CABasicAnimation *translationX = [CABasicAnimation animationWithKeyPath:@"sublayerTransform.translation.x"];
    translationX.toValue = [NSNumber numberWithFloat:multiplier * halfWidth];

    CABasicAnimation *translationZ = [CABasicAnimation animationWithKeyPath:@"sublayerTransform.translation.z"];
    translationZ.toValue = [NSNumber numberWithFloat:-halfWidth];

    CABasicAnimation *rotationY = [CABasicAnimation animationWithKeyPath:@"sublayerTransform.rotation.y"]; 
    rotationY.toValue = [NSNumber numberWithFloat: multiplier * M_PI_2];

    group.animations = [NSArray arrayWithObjects: rotationY, translationX, translationZ, nil];
    group.fillMode = kCAFillModeForwards; 
    group.removedOnCompletion = NO;

    [CATransaction flush];
    [transformationLayer addAnimation:group forKey:kAnimationKey];

}

- (void) constructRotationLayer
{
//    UIView *source = (UIView *) super.sourceViewController;
//    UIView *dest = (UIView *) super.destinationViewController;
    hostView = source.superview;

    transformationLayer = [CALayer layer];
    transformationLayer.frame = hostView.bounds;
    transformationLayer.anchorPoint = CGPointMake(0.5f, 0.5f);
    CATransform3D sublayerTransform = CATransform3DIdentity; 
    sublayerTransform.m34 = 1.0 / -1000;
    [transformationLayer setSublayerTransform:sublayerTransform];   
    [hostView.layer addSublayer:transformationLayer];

    CATransform3D transform = CATransform3DMakeTranslation(0, 0, 0);
    [transformationLayer addSublayer:[self createLayerFromView:source transform:transform]];

    transform = CATransform3DRotate(transform, M_PI_2, 0, 1, 0);
    transform = CATransform3DTranslate(transform, hostView.frame.size.width, 0, 0);
    if (!goesForward) 
    {
        transform = CATransform3DRotate(transform, M_PI_2, 0, 1, 0);
        transform = CATransform3DTranslate(transform, hostView.frame.size.width, 0, 0);
        transform = CATransform3DRotate(transform, M_PI_2, 0, 1, 0);
        transform = CATransform3DTranslate(transform, hostView.frame.size.width, 0, 0);
    }

    [transformationLayer addSublayer:[self createLayerFromView:dest transform:transform]];
}

- (void)perform
{
    [self constructRotationLayer];
    [self animateWithDuration:0.4f];
}

Solution

  • At last solved my problem, messed up with anchor point

    - (UIImage *)screenShot: (UIView *) aView
    {
        // Arbitrarily masks to 40%. Use whatever level you like
        UIGraphicsBeginImageContext(hostView.frame.size);
        [aView.layer renderInContext:UIGraphicsGetCurrentContext()];
        UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
        CGContextSetRGBFillColor (UIGraphicsGetCurrentContext(), 0, 0, 0, 0.4f);
        CGContextFillRect (UIGraphicsGetCurrentContext(), hostView.frame);
        UIGraphicsEndImageContext();
        return image;
    }
    
    
    - (CALayer *) createLayerFromView: (UIView *) aView transform: (CATransform3D) transform
    {
        CALayer *imageLayer = [CALayer layer];
        imageLayer.anchorPoint = CGPointMake(1.0f, 1.0f);
        imageLayer.frame = (CGRect){.size = hostView.frame.size};
        imageLayer.transform = transform;  
        UIImage *shot = [self screenShot:aView];
        imageLayer.contents = (__bridge id) shot.CGImage;
        return imageLayer;
    }
    
    - (void)animationDidStart:(CAAnimation *)animation 
    {
    //    UIView *source = (UIView *) super.sourceViewController;
        [source removeFromSuperview];
    }
    
    - (void)animationDidStop:(CAAnimation *)animation finished:(BOOL)finished 
    {
    //    UIView *dest = (UIView *) super.destinationViewController;
        if (hostView !=nil) {
            NSLog(@"hostView %@",hostView);
            if (dest) {
        [hostView addSubview:dest];              
            }
        }
    
        [transformationLayer removeFromSuperlayer];
        if (delegate)
            SAFE_PERFORM_WITH_ARG(delegate, @selector(segueDidComplete), nil);
    
    }
    
    -(void)animateWithDuration: (CGFloat) aDuration
    {
        CAAnimationGroup *group = [CAAnimationGroup animation]; 
        group.delegate = self; 
        group.duration = aDuration; 
    
        CGFloat halfWidth = hostView.frame.size.width / 2.0f;
        float multiplier = goesForward ? -1.0f : 1.0f;
    
        CABasicAnimation *translationX = [CABasicAnimation animationWithKeyPath:@"sublayerTransform.translation.x"];
        translationX.toValue = [NSNumber numberWithFloat:multiplier * halfWidth];
    
        CABasicAnimation *translationZ = [CABasicAnimation animationWithKeyPath:@"sublayerTransform.translation.z"];
        translationZ.toValue = [NSNumber numberWithFloat:-halfWidth];
    
        CABasicAnimation *rotationY = [CABasicAnimation animationWithKeyPath:@"sublayerTransform.rotation.y"]; 
        rotationY.toValue = [NSNumber numberWithFloat: multiplier * M_PI_2];
    
        group.animations = [NSArray arrayWithObjects: rotationY, translationX, translationZ, nil];
        group.fillMode = kCAFillModeForwards; 
        group.removedOnCompletion = NO;
    
        [CATransaction flush];
        [transformationLayer addAnimation:group forKey:kAnimationKey];
    
    }
    
    - (void) constructRotationLayer
    {
    //    UIView *source = (UIView *) super.sourceViewController;
    //    UIView *dest = (UIView *) super.destinationViewController;
        hostView = source.superview;
    
        transformationLayer = [CALayer layer];
        transformationLayer.frame = hostView.bounds;
        transformationLayer.anchorPoint = CGPointMake(0.5f, 0.5f);
        CATransform3D sublayerTransform = CATransform3DIdentity; 
        sublayerTransform.m34 = 1.0 / -1000;
        [transformationLayer setSublayerTransform:sublayerTransform];   
        [hostView.layer addSublayer:transformationLayer];
    
        CATransform3D transform = CATransform3DMakeTranslation(0, 0, 0);
        [transformationLayer addSublayer:[self createLayerFromView:source transform:transform]];
    
        transform = CATransform3DRotate(transform, M_PI_2, 0, 1, 0);
        transform = CATransform3DTranslate(transform, hostView.frame.size.width, 0, 0);
        if (!goesForward) 
        {
            transform = CATransform3DRotate(transform, M_PI_2, 0, 1, 0);
            transform = CATransform3DTranslate(transform, hostView.frame.size.width, 0, 0);
            transform = CATransform3DRotate(transform, M_PI_2, 0, 1, 0);
            transform = CATransform3DTranslate(transform, hostView.frame.size.width, 0, 0);
        }
    
        [transformationLayer addSublayer:[self createLayerFromView:dest transform:transform]];
    }
    
    - (void)perform
    {
        [self constructRotationLayer];
        [self animateWithDuration:0.4f];
    }