UIView 动画表现怪异

UIView animation acting weird

我的视图控制器的视图有一个子视图 - 一个 "login" 分类面板,有两个输入文本字段 - 位于其中心,在 Interface Builder 中设置了自动布局约束。视图控制器 class 也有一个出口集来引用垂直约束并在 运行 时间操纵它。

启动时 -viewDidLayoutSubviews()-,我将垂直约束的值缓存在 属性 (constraintInitialValue) 中,计算一个将面板隐藏在视图边界下方的值(基于面板和视图的大小)并立即应用该值,在用户看到视图之前有效地隐藏面板。我还将这个计算得出的 "off-screen" 约束值缓存在另一个 属性 (constraintOffscreenValue) 中,供以后使用(例如 "hide" 面板)。

(我在 viewDidLayoutSubviews() 中完成了所有这些初始设置,因为这是获得视图控制器视图实际边界的第一个机会。)

(为了记录,原始约束值是 0.0:中心 Y,没有偏移。并且,对于 iPhone 6 和我的面板的当前大小, "offscreen value" 是 -453.0。)

在后台,我验证了用户。如果失败,我将登录面板设置回原来的位置(屏幕中央)。

到目前为止,还不错。

接下来,当用户输入他们的凭据并点击 return 密码键(最后一个输入字段)时,我执行一些本地验证(例如,字符串不为空)和 "dismiss" 通过将面板动画化回其屏幕外位置。如果我用下面的代码来做:

UIView.animateWithDuration(NSTimeInterval(0.3),
            delay:NSTimeInterval(0.0),
            options:UIViewAnimationOptions.CurveEaseOut,
            animations: { () -> Void in

                self.panelVerticalSpaceConstraint.constant = self.constraintOffscreenValue
                self.view.layoutIfNeeded()
            },
            completion: { (finished) -> Void in

            }
        )

(viewDidLayoutSubviews() 在动画期间被调用了几次,但从第二次调用开始我的代码立即 returns。我上面提到的初始设置确实 没有被执行不止一次)

我的面板稍微向上移动,然后以相同的幅度向下移动,在屏幕中央结束(相反,它应该在最底部消失)。

如果我尝试将动画持续时间从 0.3 更改为 10.0(以正确查看正在发生的事情),而不是将面板快速 "jumps" 更改为 almost 在视图的上边缘之上,然后慢慢地动画回到中心。也就是说,如果没有发生初始跳转,我将获得所需的结果(移动到视图底部)。

有什么建议吗?提前致谢...

附录: 如果我没有尝试向下设置面板动画,而是立即将约束设置为其屏幕外值,如下所示:

self.panelVerticalSpaceConstraint.constant = self.constraintOffscreenValue
self.view.layoutIfNeeded()

...它立即消失,然后立即弹回视野中心! 那个动画是从哪里来的?约束以某种方式 "resisting change"?

好的,我找到了答案。对于一些奇怪的命运怪癖,我只能在向SO发布问题后发现我的错误(诅咒我一百万次!)。

事实证明另一个单独的动画正在同时执行(因此干扰)我的从面板下方到屏幕底部下方动画:

我碰巧有一个类似的动画,当键盘出现时向上调整面板的位置("duck the keyboard" 动画),当键盘消失时再次向下调整面板的位置,但仅在实际发生重叠的设备上(即,较小的屏幕),通过对照主视图和面板的尺寸检查键盘尺寸(通过 UIKeyboardWillShowNotification 通知传递)。

我完全忘记了那个,因为我在 iPhone 6 模拟器上进行测试,屏幕尺寸只导致最小的(几乎察觉不到,但仍然存在)键盘重叠,因此面板调整动画。当我在 iPhone 6+ 模拟器上尝试代码时(由于没有发生重叠,键盘回避动画被完全跳过),问题完全消失,所有动画的行为如下预期。

我的代码不再需要这个 restore-panel-after-ducking-the-keyboard 动画,因为关闭键盘的唯一方法是在有效凭据具有时已输入,然后开始验证(将面板一直向下,屏幕外)。仅当输入的凭据在服务器端验证失败时,该面板才会重新出现。