【问题标题】:CABasicAnimation not animating my propertyCABasicAnimation 没有为我的属性设置动画
【发布时间】:2011-03-18 02:15:57
【问题描述】:

我一直试图了解我的动画有什么问题,但我仍然没有弄清楚。我认为它应该非常直截了当,但即使在阅读了大量示例和文档之后,我也可能遗漏了一些东西。

我的问题最初来自这样一个事实,即在 iPhone 上,您无法自动调整图层大小(使用视图)。文档另有说明,但 SDK 中的图层没有 autoresizeMask。所以我决定做一些解决方法并自己为图层设置动画。

我有这段简单的代码,它应该做一个简单的调整大小动画。这些值很好,我什至设置了委托以跟踪动画是否开始/结束。

// I've got a property named layerImage (which is a CALayer)
- (void)animateTestWithFrame:(CGRect)value {
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"layerImage.frame"];
    animation.duration = 1;
    animation.fromValue = [NSValue valueWithCGRect:self.frame];
    animation.toValue = [NSValue valueWithCGRect:value];
    animation.removedOnCompletion = YES;
    animation.delegate = self;

    [self.layer addAnimation:animation forKey:@"layerImage.frame"];
}

那么,有什么想法吗? (包含代码的这个视图是窗口的子视图的子视图,如果这可以产生影响的话)

--- 编辑 ---

似乎框架不能通过 CABasicAnimation 和命名属性“frame”设置动画。使用边界时,我得到了一些奇怪的结果,但至少我得到了一些东西。将继续对此进行调查。

【问题讨论】:

    标签: iphone animation core-animation properties


    【解决方案1】:

    因此,您已经在这里解决了问题,但您对自己问题的回答有些不准确。让我纠正一些事情:

    frame 属性可以设置动画——只是不能使用显式动画。如果您对视图的根层以外的层执行以下操作,则框架的动画效果会很好:

    [CATransaction begin];
    [CATransaction setAnimationDuration:2.0f];
    [animationLayer setFrame:CGRectMake(100.0f, 100.0f, 100.0f, 100.0f)];
    [CATransaction commit];
    

    请记住,在图层上设置属性会默认为该属性更改设置动画。事实上,如果您不希望属性更改动画,则必须禁用动画。 (当然,只有在为视图的根层以外的层设置动画时才适用。)如果您需要使用显式动画,则对位置和边界进行动画处理是正确的。

    您可以使用隐式动画为 UIView 上的框架设置动画:

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:3.0f];
    [[self view] setFrame:CGRectMake(45.0f, 45.0f, 100.0f, 100.0f)];
    [UIView commitAnimations];
    

    这将从视图的当前帧(边界和位置)动画到 x = 45.0、y = 45.0、w = 100.0、h = 100.0。

    您似乎也误解了动画和图层之间的区别。您向图层添加动画,但向图层添加动画不会自动设置您正在制作动画的属性。

    CALayers 是模型对象。它们包含有关最终渲染到屏幕的图层的信息。如果您希望该属性实际具有该值,您必须设置该图层的属性。如果您只是简单地为属性设置动画,则它只是一种视觉表示,而不是实际的——也就是说,这就是为什么值会突然恢复到图层的原始值,因为您实际上从未更改过它。

    这将我引向下一点。你说:

    使用“动画.removedOnCompletion = 不;动画.fillMode = kCAFillModeForwards;" 以确保 值不会在最后重置 动画的

    这并不完全正确。这两个值只会使动画在视觉上保持在其最终位置,但是,图层的实际值并没有改变。它们仍然与您开始动画时的值完全相同。通常(除了少数例外)您不想使用这两个参数,因为它们是仅可视的。您想要的是为您正在制作动画的属性实际设置图层值。

    例如,假设您想使用显式动画为图层的位置设置动画。这是你想要的代码:

    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
    [animation setFromValue:[NSValue valueWithCGPoint:CGPointMake(70.0f, 70.0f)]];
    [animation setToValue:[NSValue valueWithCGPoint:CGPointMake(150.0f, 150.0f)]];
    [animation setDuration:2.0f];
    
    // Actually set the position on the *layer* that you want it to be
    // when the animation finishes.
    [animationLayer setPosition:CGPointMake(150.0f, 150.0f)];
    
    // Add the animation for the position key to ensure that you
    // override the animation for position changes with your animation.
    [animationLayer addAnimation:animation forKey:@"position"];
    

    您可能还需要考虑动画分组。使用动画组,您可以将多个动画组合在一起,然后控制它们相互关联的方式。在您的情况下,您的边界和位置动画的持续时间是相同的,因此您尝试做的事情在没有组的情况下可以正常工作,但是如果您想偏移动画的开始,例如您不想要位置动画开始直到一两秒进入帧动画,您可以通过设置位置动画的 beginTime 值来错开它们。

    最后,我很想知道为什么你不能使用 UIView 上可用的隐式动画。我在编写的绝大多数核心动画代码中都使用了这些,但不明白为什么这不适用于您的情况。

    最好的问候。

    【讨论】:

    • 感谢马特的解释。但是对于 UIView 动画问题,它只与我的图层有关。通常,我对使用 UIView 的动画没有任何问题,但在我的情况下,它会导致奇怪的行为。调整视图框架的大小不会调整 iPhone 上的图层大小(因为 iPhone 上没有自动机制,仅适用于视图和子视图)。所以,你必须“手动”完成。
    • 不错的答案!非常详细。如果可以的话,我会给你 2 分。
    【解决方案2】:

    因此,解决方案是为图层的@"bounds" 和@"position" 设置动画,因为框架在 iPhone 上是不可动画的。我花了一些时间才明白这个位置是图层的中心,并且调整边界的大小是从中心开始的,但这是容易的部分。

    所以,我在简历中所做的是:

    1. 在 setFrame 中,使用 bounds 和 position 属性创建 2 个动画。
    2. 使用“animation.removedOnCompletion = NO; animation.fillMode = kCAFillModeForwards;”以确保在动画结束时不会重置值
    3. 将代理注册到自己,以实现“animationDidStop:finished:”。看来您仍然需要设置值:“layerImage.bounds = [animation.toValue CGRectValue]; layerImage.position = [animation.toValue CGPointValue];”。

    我无法直接使用 UIView 动画系统,因为它没有在图层上执行我想要的操作。

    感谢 tadej5553 指出我在使用“addAnimation”时遇到的图层问题。所以这里是那些想看看它是什么样子的人的代码。

    - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
        CABasicAnimation *animation = (CABasicAnimation*)anim;
    
        if ([animation.keyPath isEqualToString:@"bounds"]) {
            layerImage.bounds = [animation.toValue CGRectValue];
        } else if ([animation.keyPath isEqualToString:@"position"]) {
            layerImage.position = [animation.toValue CGPointValue];
        }
    }
    
    - (void)setFrame:(CGRect)value {
        CGRect bounds = CGRectMake(0, 0, value.size.width, value.size.height);
    
        if ([UIView isAnimationStarted]) {
            // animate the bounds
            CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"bounds"];
            animation.duration = [UIView animationDuration];
            animation.fromValue = [NSValue valueWithCGRect:layerImage.bounds];
            animation.toValue = [NSValue valueWithCGRect:bounds];
            animation.removedOnCompletion = NO;
            animation.fillMode = kCAFillModeForwards;
            animation.timingFunction = [UIView animationFunction];
            animation.delegate = self;
            [layerImage addAnimation:animation forKey:@"BoundsAnimation"];
    
            // animate the position so it stays at 0, 0 of the frame.
            animation = [CABasicAnimation animationWithKeyPath:@"position"];
            animation.duration = [UIView animationDuration];
            animation.fromValue = [NSValue valueWithCGPoint:layerImage.position];
            animation.toValue = [NSValue valueWithCGPoint:CGPointMake(bounds.size.width / 2, bounds.size.height / 2)];
            animation.removedOnCompletion = NO;
            animation.fillMode = kCAFillModeForwards;
            animation.timingFunction = [UIView animationFunction];
            animation.delegate = self;
            [layerImage addAnimation:animation forKey:@"PositionAnimation"];
        } else {
            layerImage.frame = bounds;
        }
    
        [super setFrame:value];
    }
    

    【讨论】:

      【解决方案3】:

      键路径只能是属性的键路径,而不是对象的名称。

      使用这个

      [CABasicAnimation animationWithKeyPath:@"frame"]
      

      而不是这个

      [CABasicAnimation animationWithKeyPath:@"layerImage.frame"]
      

      顺便说一句,当您向图层添加动画时,键并不意味着动画的键属性。只是您希望此动画具有的键(名称)(这指的是您的代码的最后一行)

      【讨论】:

      • 对帧来说没问题,但是 CABasicAnimation 是如何知道要为该属性设置动画的图层的呢?这是否意味着我必须做 [layerImage addAnimation:animation ...]。 (试过了也没用)
      • 如果您在与 self 不同的对象上制作动画(在该方法中),只需使用 [self.layerImage.layer addAnimation:animation ...]
      • 还有一件事:我不明白你为什么在这里使用 CABasicAnimation。 UIView 的动画就可以了。
      • 使用 UIView 的动画似乎不正确。我又试了一次,仍然有奇怪的行为。 (没有动画,大小似乎不正确)。我会做更多测试以了解发生了什么。
      • 错误行为是我这边的一个带有 autoresizingMask 的错误。但是我仍然在调整图层的大小。使用 UIView,它完全被忽略,而使用 CABasicAnimation,它什么也不做。
      猜你喜欢
      • 1970-01-01
      • 2011-08-23
      • 1970-01-01
      • 1970-01-01
      • 2012-07-12
      • 2013-06-07
      • 2014-04-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多