3D 动画 - CABasicAnimation 和 CATransform3D 提供视图之间的差距

Posted

技术标签:

【中文标题】3D 动画 - CABasicAnimation 和 CATransform3D 提供视图之间的差距【英文标题】:3D Animation - CABasicAnimation and CATransform3D giving gap between views 【发布时间】:2012-04-09 09:10:22 【问题描述】:

所以,我正在尝试在第一个视图中的这个应用程序http://itunes.apple.com/us/app/spg-mobile-app/id312306003?mt=8 中实现一个动画

我已经实现了翻转视图的动画,并且我成功地为一侧设置动画,当我给出反向翻转的反向逻辑时,它会在视图之间产生一些差距

这是我的代码

- (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];

所以,这里我先调用 perform 方法 在这里我上传了我的总代码Here


这段代码对我有点用,但仍然有一些差距,问题是当前视图的框架和即将到来的视图应该相同

- (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];

【问题讨论】:

示例代码不完整。它不会构建,因为它缺少 RotatingFlipSegue 的文件 @Charan 我也需要那个视图。你能分享一下吗?如果您将演示代码上传到 git 并与我们分享,那就太好了。 【参考方案1】:

终于解决了我的问题,搞砸了锚点

- (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];

【讨论】:

【参考方案2】:

我也是这样做的。我的工作代码如下。请喜欢它。

- (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    = (id) shot.CGImage;

        return imageLayer;
    

    - (void)animationDidStart:(CAAnimation *)animation 
    
        UIViewController *source;
        if (islogin) 
            source = (UIViewController *)self.navigationController;
        
        else
            source = (UIViewController *)self.joinTABBARController;
        [source.view removeFromSuperview];
    

    - (void)animationDidStop:(CAAnimation *)animation finished:(BOOL)finished 
    
        UIViewController *dest;
        if (islogin) 
           dest = (UIViewController *) self.joinTABBARController; 
        
        else
            dest = (UIViewController  *)self.navigationController;
        [hostView addSubview:dest.view];
        [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:(TranDirection)aDirection
    
        UIViewController *source;
        UIViewController *dest;
        if (islogin) 
            source = (UIViewController *) self.navigationController;
            dest   = (UIViewController*)  self.joinTABBARController;
        
        else
        
            source = (UIViewController *) self.joinTABBARController;
            dest   = (UIViewController *) self.navigationController;
        
        hostView = source.view.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.view 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.view transform:transform]];

        if (aDirection == RTOL)
        
            transform = CATransform3DRotate(transform, radians(90), 0, 1, 0);
            transform = CATransform3DTranslate(transform, CUBESIZE, 0, 0);
            [transformationLayer addSublayer:[self createLayerFromView:dest.view transform:transform]];
         else 
            transform = CATransform3DRotate(transform, radians(90), 0, 1, 0);
            transform = CATransform3DTranslate(transform, CUBESIZE, 0, 0);
            transform = CATransform3DRotate(transform, radians(90), 0, 1, 0);
            transform = CATransform3DTranslate(transform, CUBESIZE, 0, 0);
            transform = CATransform3DRotate(transform, radians(90), 0, 1, 0);
            transform = CATransform3DTranslate(transform, CUBESIZE, 0, 0);
            [transformationLayer addSublayer:[self createLayerFromView:dest.view transform:transform]];

        

    

    -(void)moveFrom:(TranDirection)aDirection duration:(float)aDuration
    
        [CATransaction flush];
        CABasicAnimation *rotation;
        CABasicAnimation *translationX;
        CABasicAnimation *translationZ;
        CAAnimationGroup *group = [CAAnimationGroup animation]; 
        group.delegate = self; 
        group.duration = aDuration; 

        if (aDirection == RTOL)
        
            translationX         = [CABasicAnimation animationWithKeyPath:@"sublayerTransform.translation.x"];
            translationX.toValue = [NSNumber numberWithFloat:-(CUBE_VERTICAL_WIDTH / 2)];
            rotation             = [CABasicAnimation animationWithKeyPath:@"sublayerTransform.rotation.y"]; 
            rotation.toValue     = [NSNumber numberWithFloat:radians(-90)];
         else 
            translationX         = [CABasicAnimation animationWithKeyPath:@"sublayerTransform.translation.x"];
            translationX.toValue = [NSNumber numberWithFloat:(CUBE_VERTICAL_WIDTH / 2)];
            rotation             = [CABasicAnimation animationWithKeyPath:@"sublayerTransform.rotation.y"]; 
            rotation.toValue     = [NSNumber numberWithFloat:radians(90)] ;
        

        translationZ              = [CABasicAnimation animationWithKeyPath:@"sublayerTransform.translation.z"];
        translationZ.toValue      = [NSNumber numberWithFloat:-(CUBE_VERTICAL_WIDTH / 2)];
        group.animations          = [NSArray arrayWithObjects: rotation, translationX, translationZ, nil];
        group.fillMode            = kCAFillModeForwards; 
        group.removedOnCompletion = NO;
        [transformationLayer addAnimation:group forKey:kAnimationKey];
    

    - (void)perform:(TranDirection)tdir
    
        [self constructRotationLayer:tdir];
        // [self animateWithDuration:2.0f];
        [self moveFrom:tdir duration:.4];
    

【讨论】:

但我的问题是在反转翻转动画时,视图之间出现了差距 我必须在 z 轴上前后移动沙袋,所以在这方面有任何建议..

以上是关于3D 动画 - CABasicAnimation 和 CATransform3D 提供视图之间的差距的主要内容,如果未能解决你的问题,请参考以下文章

如何在动画 CABasicAnimation 时改变速度

如何在动画CABasicAnimation时改变速度

CABasicAnimation 代替 UIView 动画

CABasicAnimation 对象自身周围的动画(360 度)

CABasicAnimation基础核心动画

更新模型层时,CABasicAnimation 无法正确设置动画