23

为了说明这个问题,我在 Github 上提供了一个非常小的 Xcode 项目(两个类,11kb 下载)。看看这里的要点或使用git clone git@gist.github.com:93982af3b65d2151672e.git


请考虑以下情况。我的一个自定义视图,称为“容器视图”,包含两个小方块。在此屏幕截图中说明:

两个方块初始位置截图

蓝色方块是 22x22 ptUIView实例,红色方块是 22x22 ptCALayer实例。出于这个问题的目的,我希望这两个正方形“粘”到外视图的右下角,同时我为该外视图的框架设置动画。

UIView我在 a的 class method中更改了容器视图的框架animateWithDuration:delay:options:animations:completion:,并使用了 type 的非默认缓动参数UIViewAnimationCurveEaseOut

[UIView animateWithDuration:1.0 delay:0 
                    options:UIViewAnimationCurveEaseOut 
                 animations:^{ _containerView.frame = randomFrame; } 
                 completion:nil];

注意:蓝色 UIView 和红色 CALayer 的框架都是在容器视图的重写方法中设置的,但是如果我将蓝色 UIView 的属性设置为 UIView 灵活的左边距和上边距setFrame:,结果将是相同的。autoresizingMask

在生成的动画中,蓝色方块按照我的意图“粘”在角落,但红色方块完全忽略了动画的时间和缓动。我认为这是因为 Core Animation 的隐式动画特性,这个特性在很多场合都对我有帮助。

以下是动画序列中的一些屏幕截图,说明了两个方块的异步性:

动画中间的三个屏幕截图,其中红色方块不在“正确”位置

关于这个问题:有没有办法同步两个帧的变化,使红色的 CALayer 和蓝色的 UIView 都以相同的动画持续时间和缓动曲线移动,就像它们是一个视图一样粘在一起?

PS 当然,两个正方形粘在一起所需的视觉效果可以通过多种方式实现,例如让两个图层都变为CALayers 或UIViews,但真正问题所在的项目有一个非常正当的原因一个是 CALayer,另一个是 UIView。

4

2 回答 2

10

我假设您的结束位置正确,并且在动画之后视图和图层对齐。这与动画无关,只是几何。


当您更改CALayer 不是视图层的 a的属性时(例如在您的情况下),它将隐式动画为其新值。要自定义此动画,您可以使用显式动画,例如基本动画。更改基本动画中的帧看起来像这样。

CABasicAnimation *myAnimation = [CABasicAnimation animationWithKeyPath:@"frame"];
[myAnimation setToValue:[NSValue valueWithCGRect:myNewFrame]];
[myAnimation setFromValue:[NSValue valueWithCGRect:[myLayer frame]]];
[myAnimation setDuration:1.0];
[myAnimation setTimingFunction:[CAMediaTimingFuntion functionWithName: kCAMediaTimingFunctionEaseOut]];

[myLayer setFrame:myNewFrame];
[myLayer addAnimation:myAnimation forKey:@"someKeyForMyAnimation"];

如果两个动画的时间函数和持续时间相同,那么它们应该保持对齐。

另请注意,显式动画不会更改值,并且您必须同时添加动画并设置新值(如上面的示例)


是的,有很多方法可以达到相同的效果。一个示例是让视图和图层成为同一子视图的子视图(这又是外框的子视图)。

编辑

您不能轻松地将 UIView 动画与显式动画分组。您也不能使用动画组(因为您将它们应用到不同的图层),但您可以使用 CATransaction:

[CATransaction begin];
[CATransaction setAnimationDuration:1.0];
[CATransaction setAnimationTimingFunction:[CAMediaTimingFuntion functionWithName: kCAMediaTimingFunctionEaseOut]];

// Layer animation
CABasicAnimation *myAnimation = [CABasicAnimation animationWithKeyPath:@"frame"];
[myAnimation setToValue:[NSValue valueWithCGRect:myNewFrame]];
[myAnimation setFromValue:[NSValue valueWithCGRect:[myLayer frame]]];

[myLayer setFrame:myNewFrame];
[myLayer addAnimation:myAnimation forKey:@"someKeyForMyAnimation"];

// Outer animation
CABasicAnimation *outerAnimation = [CABasicAnimation animationWithKeyPath:@"frame"];
[outerAnimation setToValue:[NSValue valueWithCGRect:myNewOuterFrame]];
[outerAnimation setFromValue:[NSValue valueWithCGRect:[outerView frame]]];

[[outerView layer] setFrame:myNewOuterFrame];
[[outerView layer] addAnimation:outerAnimation forKey:@"someKeyForMyOuterAnimation"];

[CATransaction commit];
于 2012-05-29T14:53:09.220 回答
1

如果你想要 David Rönnqvist 在Swift 3.0中的片段,这里有:

    CATransaction.begin()
    CATransaction.setAnimationDuration(1.0)
    CATransaction.setAnimationTimingFunction(CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut))

    // Layer animation
    let myAnimation = CABasicAnimation(keyPath: "frame");
    myAnimation.toValue = NSValue(cgRect: myNewFrame)
    myAnimation.fromValue = NSValue(cgRect: myLayer.frame)

    myLayer.frame = myNewFrame
    myLayer.add(myAnimation, forKey: "someKeyForMyAnimation")

    // Outer animation
    let outerAnimation = CABasicAnimation(keyPath: "frame")
    outerAnimation.toValue = NSValue(cgRect: myNewOuterFrame)
    outerAnimation.fromValue = NSValue(cgRect: outerView.frame)

    outerView.layer.frame = myNewOuterFrame
    outerView.layer.add(outerAnimation, forKey: "someKeyForMyOuterAnimation")

    CATransaction.commit()
于 2017-07-07T08:52:02.983 回答