如何同步动画UIView和CALayer

epo*_*gee 21 cocoa-touch core-animation objective-c uiviewanimation

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


请考虑以下方案.我的自定义视图称为"容器视图",包含两个小方块.截图中显示:

两个正方形的初始位置的屏幕截图

蓝色方块是22x22 pt UIView实例,红色方块是22x22 pt CALayer实例.出于这个问题的目的,我希望两个正方形"粘住"到外部视图的右下角,同时我为该外部视图的框架设置动画.

我在UIView类的方法中更改容器视图的框架animateWithDuration:delay:options:animations:completion:,使用类型的非默认缓动参数UIViewAnimationCurveEaseOut.

[UIView animateWithDuration:1.0 delay:0 
                    options:UIViewAnimationCurveEaseOut 
                 animations:^{ _containerView.frame = randomFrame; } 
                 completion:nil];
Run Code Online (Sandbox Code Playgroud)

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

在生成的动画中,蓝色方块按照我的预期方式"粘贴"到角落,但红色方块完全忽略了动画的时间和缓和.我认为这是因为Core Animation的隐式动画功能,这个功能在很多场合帮助了我.

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

动画中期的三个截图,其中红色方块不在

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

PS当然,两个方块粘在一起所需的视觉效果可以通过多种方式实现,例如,通过使两个层成为CALayers或UIViews,但真正问题所涉及的项目具有非常合理的原因.一个是CALayer,另一个是UIView.

Dav*_*ist 10

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


当您更改CALayer 不属于视图层的属性时(如您的情况),它将隐式地为其新值设置动画.要自定义此动画,您可以使用显式动画,如基本动画.在基本动画中更改框架看起来像这样.

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"];
Run Code Online (Sandbox Code Playgroud)

如果计时功能和两个动画的持续时间相同,那么它们应保持对齐.

另请注意,显式动画不会更改值和帽子,您必须添加动画并设置新值(如上面的示例中所示)


是的,有很多方法可以达到同样的效果.一个示例是使视图和图层是相同子视图的子视图(其又是外框的子视图).

编辑

您无法使用显式动画轻松地对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];
Run Code Online (Sandbox Code Playgroud)