CABasicAnimation没有动画我的财产

时间:2010-07-19 19:30:45

标签: iphone animation core-animation properties

我一直试图了解我的动画有什么问题,但我还是没弄清楚。我认为这应该是非常直接的,但即使在阅读了大量的例子和文档之后,我也可能会遗漏这些内容。

我的问题最初形成的事实是,在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”进行动画处理。当使用边界时,我得到了一些奇怪的结果,但至少我得到了一些东西。将继续调查此事。

3 个答案:

答案 0 :(得分:26)

所以你在这里找到了解决方案很好,但你对自己问题的回答有些不准确。让我纠正一些事情:

框架属性可以动画 - 只是没有显式动画。如果对视图的根层以外的图层执行以下操作,则该框架将生成动画:

[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是模型对象。它们包含有关最终呈现到屏幕的图层的信息。如果您希望该属性实际具有该值,则必须设置图层的属性。如果你只是为属性设置动画,那么它只是一个直观的表示,而不是实际的 - 也就是说这就是为什么值会快速回到图层的原始值,因为你从未真正改变它。

这引导我进入下一点。你说:

  

使用“animation.removedOnCompletion =   没有; animation.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上可用的隐式动画。我在我编写的绝大多数Core Animation代码中都使用了这些代码,并且无法理解为什么这对你的情况不起作用。

最好的问候。

答案 1 :(得分:3)

密钥路径应该只是属性的关键路径,而不是对象的名称。

使用此

[CABasicAnimation animationWithKeyPath:@"frame"]

而不是

[CABasicAnimation animationWithKeyPath:@"layerImage.frame"]

而且顺便说一句,当你向一个图层添加动画时,该键并不意味着动画的关键属性。只是你想要这个动画的键(名称)(这是指代码的最后一行)

答案 2 :(得分:1)

因此,解决方案是设置图层的@“边界”和@“位置”的动画,因为在iPhone上帧不可动画。我花了一些时间才明白位置是图层的中心,边界的大小是从中心延伸出来的,但这很容易。

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

  1. 在setFrame中,使用bounds和position属性创建2个动画。
  2. 使用“animation.removedOnCompletion = NO; animation.fillMode = kCAFillModeForwards;”确保在动画结束时不重置值
  3. 将委托注册到self以实现“animationDidStop:finished:”。您似乎仍需要设置值:“layerImage.bounds = [animation.toValue CGRectValue]; layerImage.position = [animation.toValue CGPointValue];”。
  4. 我无法直接使用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];
    }