ios - 稍后添加 subview 以与 super View 一起动画

标签 ios uiview calayer uiviewanimation

我有一个容器 View ——我们称之为套接字 View ——它有一个 subview ,它是内容 View ——我们称之为插件 View 。此插头 View 可以为零,即 socket View 当前为空。如果它确实包含一个插头 View ,它会占用整个 socket 的空间,即它的框架是 socket 的边界。从外部的角度来看,您甚至不应该知道实际上有两个 View ,因为插头 View 始终恰好位于 socket 所在的位置。

我正在努力让动画正常工作:如果插件 View 存在并且在动画之前布局,一切都按预期工作。但是,如果我仅在动画已运行时才设置 socket 的插头 View ,则会产生不希望的效果:

插头 View 被布置到动画结束时的位置,并且不会在其 socket 旁边进行动画处理。我希望它看起来一直在那里,但只是现在才可见,即插头 View (及其 subview )应该在 socket 旁边设置动画,即使我在动画进行时添加它也是如此。

我怎样才能实现这种行为?

我的想法:显然,插头 View 必须布置两次:一次用于其最终位置,一次用于 socket View 开始动画的位置或已添加的位置。我可以计算这个帧,在没有动画的情况下应用它,并在一个新的动画块中为最后一帧设置动画。为了使动画时间保持一致,我需要具有相同的曲线和持续时间,但在过去开始动画或以某种方式转发它。这可能吗?是否有其他方法可以让插头 View 始终保持全宽和全高?

作为 Rob 回答的后续行动,以下是我正在寻找的更多细节:

  • 套接字 View 具有动画效果,因为其所有者的边界大小已更改。您可以将其视为表格 View 中的全角单元格。
  • 插件 View 可能包含它自己的 subview ,比如 ImageView 、标签等。这些也应该加入到套接字 View 的动画中,就好像它们从动画开始就一直存在一样。
  • 虽然理论上有可能在一个新动画已经运行时开始新动画,但我并不介意这种极端情况下的行为。
  • 用户不必在动画运行时与插件 View 交互;无论如何,这很可能发生在界面方向更改期间。
  • 由于动画时的异步模型更新,插件 View 可能决定更改其内容,但这又是一个边缘情况,我不介意在这种情况下动画看起来不完美。然而,它的大小不会改变——它总是与插头 View 的大小相同。
  • 最佳答案

    You can find my test project here.

    你说你的插头 View 应该完全覆盖 socket View 。我们需要担心两件事:插头的层位置( layer.position )和层大小( layer.bounds.size )。

    默认情况下,position控制图层(和 View )的中心,因为默认 anchorPoint是 (0.5, 0.5)。如果我们设置 anchorPoint到 (0, 0),然后 position控制图层的左上角,我们总是希望它在套接字坐标系中的 (0, 0) 处。因此,通过同时设置 anchorPointpositionCGPointZero ,我们可以避免担心动画layer.position .

    这只是让我们制作动画 layer.bounds.size .

    当您使用 UIKit 动画为 View 设置动画时,它会创建 CABasicAnimation 的实例。在引擎盖下。 CABasicAnimationCAAnimation 的子类添加(除其他外)fromValuetoValue属性,指定动画的开始和结束值。
    CAAnimation符合CAMediaTiming协议(protocol),这意味着它有一个 beginTime属性(property)。当您创建 CAAnimation ,它有一个默认值 beginTime零。 Core Animation 在提交当前事务时将其更改为当前时间(请参阅 CACurrentMediaTime )。

    但是如果动画已经有一个非零值 beginTime , Core Animation 按原样使用它。如果那 beginTime是过去的,那么动画在第一次出现在屏幕上时已经部分(甚至完全)完成,并且绘制时已经取得了适当的进展。我们基本上可以“回溯”动画。

    所以如果我们挖出 CABasicAnimation控制套接字的 bounds.size , 并将其添加到插件中,插件应该完全按照我们想要的方式制作动画。当我们将动画附加到插头时,它的 beginTime是它开始动画套接字的时间。因此,即使我们稍后将其连接到插头上,它也会与 socket 完美同步。由于我们希望插头和 socket 具有相同的尺寸,fromValuetoValue也已经正确。

    在我的测试应用程序中,我将 socket 设为粉红色,将插头设为蓝色。每个都是UIImageView在边缘显示带有线条的图像,因此我们可以确保 View 始终具有正确的尺寸。这是它的实际运行情况:

    simple demo

    还有一个进一步的转折。如果您为已经在设置动画的 View 设置动画,UIKit 不会停止之前的动画。它添加了第二个动画,并且两个动画同时运行。

    这是有效的,因为 UIKit 使用附加动画。当你将一个 View 的宽度从 320 动画化到 160 时,UIKit 会立即将宽度设置为 160,并添加一个从 160 到 0 的附加动画。在动画开始时,表观宽度为 160+160=320,在最后,表观宽度为 160+0=160。

    当 UIKit 在第一个动画运行时添加第二个动画时,两个动画的值都会添加到用于在屏幕上绘制 View 的表观值。效果如下:

    multiple animations

    这对我们来说意味着我们必须寻找所有带有 keyPath 的套接字动画。的 position.size ,并将它们全部复制到插件中。这是我的测试应用程序中的代码:

    - (IBAction)plugWasTapped:(id)sender {
        if (self.plugView.superview) {
            [self.plugView removeFromSuperview];
            return;
        }
    
        self.plugView.frame = self.socketView.bounds;
        [self.socketView addSubview:self.plugView];
    
        for (NSString *key in self.socketView.layer.animationKeys) {
            CAAnimation *rawAnimation = [self.socketView.layer animationForKey:key];
            if (![rawAnimation isKindOfClass:[CABasicAnimation class]]) {
                continue;
            }
    
            CABasicAnimation *animation = (CABasicAnimation *)rawAnimation;
            if ([animation.keyPath isEqualToString:@"bounds.size"]) {
                [self.plugView.layer addAnimation:animation forKey:key];
            }
        }
    }
    

    这是多个同步动画的结果:

    demo with multiple animations

    更新

    坦率地说,让插件 View 的完整 View 层次结构动画化,就好像它一开始就在那里一样。

    这是一种选择:
  • 以 socket 的原始尺寸布置插头 View 并创建它的图像(“开始图像”)。
  • 以 socket 的最终尺寸布置插头 View 并创建它的图像(“最终图像”)。
  • 将占位符 ImageView 放入套接字。
  • 将大小动画从套接字复制到占位符。
  • 使用大小动画在占位符上创建内容动画,将其内容从开始图像淡入到结束图像。
  • 当动画结束时,用插头 View 替换占位符。

  • 这是它的样子:

    crossfade demo

    除了交叉淡入淡出的缺陷之外,这个版本不能处理同时运行的多个动画。您可以在 下找到它淡入淡出我的存储库中的标签(链接在顶部)。

    另一种方法是仅以最终大小呈现插头 View ,然后将其放入占位符中而没有交叉淡入淡出。它看起来像这样:

    stretch end image demo

    我认为这样看起来更好,这个版本处理堆叠动画。您可以在 下找到它拉伸(stretch)结束图像在我的存储库中标记。

    最后,我没有实现一种完全不同的方法。这仅适用于您自己创建的调整大小动画——它不适用于系统在方向更改时创建的旋转动画。您可以使用计时器简单地为您的套接字 View 的边界设置动画,而不是使用 UIKit 动画。 WWDC 2012 Session 228: Best Practices for Mastering Auto Layout讨论到最后。他建议使用 NSTimer但我认为你最好使用 CADisplayLink .如果你采用这种方法,插件 View 的 subview 都将完美地动画。它比使用 UIKit 动画要多一些工作,但应该很容易实现。

    关于ios - 稍后添加 subview 以与 super View 一起动画,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33399455/

    相关文章:

    iOS 正弦波生成 - 可听见的点击

    ios - 使用 UIAlertView 取消触摸

    swift - 在呈现给新 View Controller 之前关闭 View Controller

    ios - 我们如何将 Circle progress 绘制为 "three fourths"- 3/4?

    ios - 使用 Swift 在 iOS 中平滑搜索栏动画

    ios - ios 7中的区域监控

    ios - subview 的位置不是我所期望的

    ios - 在调整 scrollView 的框架大小时,UIScrollView subview 的大小调整不正确

    ios - 育儿时如何覆盖CALayers的绘图顺序

    ios - 在 iOS 中使用 Core 动画打开纸张动画