如何使用CAKeyframeAnimation动画化CATransform3D?

13

我使用 CAKeyframeAnimations 来动画化一个图层的 transform.rotation 和 transform.translation.x 属性,但我尝试使用隐式动画来动画化 transform 属性时出现了问题。我有一个图层必须在两个状态之间进行动画,并且 CABasicAnimation 的默认插值完全不正确,无法按照我想要的路径进行运动。CAKeyframeAnimation 看起来能解决这个问题,但实际上并非如此。任何尝试使用 CAKeyframeAnimation 动画化 transform 的操作都会导致视图立即跳转到最终的变换状态,而其他动画则继续运行。如果我删掉下面这个函数的前半部分,并让我的 "transform" 事件在底部使用 CABasicAnimation,那么它就可以正常地进行动画 - 尽管在一些错误的插值变换过程中。

我的图层委托实现了以下内容:

- (id <CAAction>) actionForLayer:(CALayer *)layer forKey:(NSString *)event
{    
    if ([event isEqualToString:@"transform"])
    {
        CGSize startSize = ((CALayer *)self.layer.presentationLayer).bounds.size;
        CGSize endSize = self.layer.bounds.size;

        CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:event];
        animation.duration = 0.25;

        NSMutableArray *values = [NSMutableArray array];

        int stepCount = 10;
        for (int i = 0; i < stepCount; i++)
        {
            CGFloat p = i / (float)(stepCount - 1);
            CGSize size = [self interpolateBetweenSize:startSize andSize:endSize percentage:p];
            CATransform3D transform = [self transformForSize:size];
            [values addObject:[NSValue valueWithCATransform3D:transform]];
        }

        animation.values = values;
        return animation;
    }

    // All other animations use this basic animation
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:event];
    animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    animation.removedOnCompletion = YES;
    animation.fillMode = kCAFillModeForwards;
    animation.duration = 0.25;
    return animation;
}

我的变换是先进行平移再进行旋转,但我认为使用一组动画和单独的关键帧动画来通过平移和旋转进行动画处理会导致混乱。我已确认对于我传递的所有p值,大小和变换都是正确的,并且p严格地从0到1范围内。

我尝试设置非默认的timing function,尝试设置一个timing function数组,省略了keyTimes,设置了重复次数为0(repeatCount),removedOnCompletion=YES,以及fillMode为forwards,但这些都没有效果。我是否没有正确创建变换关键帧动画?


1
你能否澄清一下你想要实现的确切效果?你描述它为先进行翻译,然后进行旋转,但从你的代码来看,边界也在动画化 - 这是正确的吗?你想让图层移动,然后旋转,同时在移动/旋转动画的两个阶段中不断增长或缩小边界? - GSnyder
为什么需要隐式呢?这段代码看起来很不专业。如果你想要精细的控制,为什么不直接创建动画并将其直接添加到图层中,而你说你已经做到了呢?此外,如果您希望使用默认动画,请从该委托方法中返回 nil - Paul.s
2个回答

0
(void)animateViewWith3DCurrentView:(UIView *)currentView withPoing:(CGPoint)movePoint
{
    //flip the view by 180 degrees in its place first.
    currentView.layer.transform =          CATransform3DRotate(currentView.layer.transform,myRotationAngle(180), 0, 1, 0);

    //set the anchor point so that the view rotates on one of its sides.
    currentView.layer.anchorPoint = CGPointMake(0.5, 0.5);

    //Set up scaling

    CABasicAnimation *resizeAnimation = [CABasicAnimation animationWithKeyPath:kResizeKey];

    //we are going to fill the screen here. So 423,337
    [resizeAnimation setToValue:[NSValue valueWithCGSize:CGSizeMake(423, 337)]];
    resizeAnimation.fillMode            = kCAFillModeForwards;
    resizeAnimation.removedOnCompletion = NO;

     // Set up path movement

    UIBezierPath *movePath = [UIBezierPath bezierPath];

    //the control point is now set to centre of the filled screen. Change this to make the path different.
    // CGPoint ctlPoint       = CGPointMake(0.0, 0.5);
    CGPoint ctlPoint       = CGPointMake(1024/2, 768/2);

    //This is the starting point of the animation. This should ideally be a function of the frame of the view to be animated. Hardcoded here.

    // Set here to get the accurate point..
    [movePath moveToPoint:movePoint];

    //The anchor point is going to end up here at the end of the animation.
    [movePath addQuadCurveToPoint:CGPointMake(1024/2, 768/2) controlPoint:ctlPoint];

    CAKeyframeAnimation *moveAnim = [CAKeyframeAnimation animationWithKeyPath:kPathMovement];

    moveAnim.path                = movePath.CGPath;
    moveAnim.removedOnCompletion = YES;

     // Setup rotation animation

    CABasicAnimation* rotateAnimation = [CABasicAnimation animationWithKeyPath:kRotation];
    //start from 180 degrees (done in 1st line)
    CATransform3D fromTransform       = CATransform3DMakeRotation(myRotationAngle(180), 0, 1, 0);
    //come back to 0 degrees
    CATransform3D toTransform         = CATransform3DMakeRotation(myRotationAngle(0), 0, 1, 0);

    //This is done to get some perspective.
    CATransform3D persp1 = CATransform3DIdentity;
    persp1.m34 = 1.0 / -3000;

    fromTransform = CATransform3DConcat(fromTransform, persp1);
    toTransform = CATransform3DConcat(toTransform,persp1);

    rotateAnimation.toValue             = [NSValue valueWithCATransform3D:toTransform];
    rotateAnimation.fromValue           = [NSValue valueWithCATransform3D:fromTransform];
    //rotateAnimation.duration            = 2;
    rotateAnimation.fillMode            = kCAFillModeForwards;
    rotateAnimation.removedOnCompletion = NO;


     // Setup and add all animations to the group

    CAAnimationGroup *group = [CAAnimationGroup animation]; 

    [group setAnimations:[NSArray arrayWithObjects:moveAnim,rotateAnimation, resizeAnimation, nil]];

    group.fillMode            = kCAFillModeForwards;
    group.removedOnCompletion = NO;
    group.duration            = 0.7f;
    group.delegate            = self;

    [group setValue:currentView forKey:kGroupAnimation];

    [currentView.layer addAnimation:group forKey:kLayerAnimation];   
}

0

这种技术在iOS 3中有效,但在iOS 5.0中似乎出现了问题。

5.1版本“神奇地”修复了这个问题,看起来是iOS 5.0的一个bug。我本来想提交一个radar,但在5.1中已经解决了。

@Gsnyder:一些背景信息:我正在尝试使用类似Clear的UI(与Clear完全无关),并想出了这个:http://blog.massivehealth.com/post/18563684407/clear。这应该解释了旋转和平移的必要性。

我后来创建了一个百叶窗过渡效果,将视图细分为N层(而不仅仅是2层),从侧面看起来像这样://///。

我的代码没有对边界进行动画处理,而是在每个步骤中使用大小来确定所需的变换。

@Paul.s:隐式允许我在图层类本身内保持这种抽象,而不会污染拥有它的视图控制器。视图控制器应该只是改变边界,而图层应该相应地移动。我不喜欢视图控制器有数十个自定义动画,而视图本身可以处理它。

我需要使用关键帧动画,因为在层变换之间的默认动画 / 和 _ 通过不正确的角度进行动画,所以 ///\ 层在整个变换过程中无法对齐。关键帧动画可以确保所有边缘都正确对齐,同时它们都进行动画。

我认为这个问题已经解决了,这似乎是 iOS 5.0 中的一个错误,现在已经被修复了。谢谢大家。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接