2010-07-19 51 views
7

我一直在试图了解我的动画有什么问题,我仍然没有弄明白。我认为它应该是非常简单的,但即使在阅读了大量的例子和文档之后,我可能仍然缺少某些东西。CABasicAnimation没有动画我的财产

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

回答

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 = NO; animation.fillMode = kCAFillModeForwards;”确保 值不会在动画

这结束 reseted是不完全正确的。这两个值只是简单地使动画保持在最终位置可视,但是,该图层的实际值没有改变。它们仍然与开始动画时的值完全相同。一般情况下(除少数例外),您不想使用这两个参数,因为它们只是只可视为。你想要的是实际设置你的动画属性的图层值。

举例来说,你想要使用明确的动画来动画你的图层的位置。这里是你想要的代码:

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代码中使用这些代码,并且无法明白为什么这不适合您的情况。

此致敬礼。

+0

感谢您的解释马特。但对于UIView动画问题,它仅与我的图层有关。通常,我使用UIView的动画没有问题,但在我的情况下,它造成了奇怪的行为。调整视图框的大小并不会调整iPhone的图层大小(因为iPhone上没有自动机制,仅适用于视图和子视图)。所以,你必须“手动”。 – Sauleil 2010-07-21 14:00:14

+0

好的答案!很详细。如果可以的话,我会给你2分。 – Soberman 2016-01-18 22:26:12

3

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

使用此

[CABasicAnimation animationWithKeyPath:@"frame"] 

,而不是这个

[CABasicAnimation animationWithKeyPath:@"layerImage.frame"] 

而就顺便说一句,当你添加动画层,关键doen't意味着关键属性动画。只是你想要这个动画的关键(名字)(这是指你的代码的最后一行)

+0

确定框架,但CABasicAnimation如何知道哪一层来动画该属性?这是否意味着我必须做[layerImage addAnimation:animation ...]。 (尝试它,也不工作) – Sauleil 2010-07-19 19:41:25

+0

如果你在一个不同的对象上进行动画(在该方法中),只需使用[self.layerImage.layer addAnimation:animation ...] – tadejsv 2010-07-19 19:49:52

+0

还有一件事:I不明白你为什么在这里使用CABasicAnimation。 UIView的动画可以。 – tadejsv 2010-07-19 19:52:12

1

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

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

  1. 在SETFRAME,创建2个动画与边界和位置属性。
  2. 使用“animation.removedOnCompletion = NO; animation.fillMode = kCAFillModeForwards;”以确保值不会在动画结束时重置
  3. 将委托注册为self以便实现“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]; 
}