如果调用layoutIfNeeded(),UIView约束将不会生成动画

时间:2017-05-02 13:23:26

标签: ios swift cocoa animation uiview

我正在尝试将受约束的视图(iconVerticalConstraint)设置为超视图的中心Y(+100)的动画,特别是在到达其右侧(0)后的位置动画。 这是我的代码:

self.view.layoutIfNeeded()

UIView.animateKeyframes(withDuration: 2, delay: 0, animations: {

    UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: 0.25, animations:{
        self.iconVerticalConstraint.constant -= 20
    })

    UIView.addKeyframe(withRelativeStartTime: 0.25, relativeDuration: 0.75, animations:{
        self.iconVerticalConstraint.constant -= 80
    })
})

我尝试关注this question's answer,但这没有帮助,因为在我的情况下,我的视图包含更多子视图,内在链接,并且在我的动画块中放置layoutIfNeeded()会为整个<设置动画/ strong>查看,而不仅仅是那个约束。否则这种方法根本就没有动画。

2 个答案:

答案 0 :(得分:2)

更改

等约束
self.iconVerticalConstraint.constant -= 20

应该在动画块外面,你只需要调用

self.view.layoutIfNeeded() 

我认为因为你试图做2,并且打电话

self.view.layoutIfNeeded() 

它们都发生一次,考虑改为:

    self.iconVerticalConstraint.constant -= 20

    UIView.animate(withDuration: 0.25, animations: {
        self.view.layoutIfNeeded()
    }) { (finished) in

        self.iconVerticalConstraint.constant -= 80

        UIView.animate(withDuration: 0.75, animations: {
            self.view.layoutIfNeeded()
        }, completion: nil)
    }

编辑:说明

当您遇到约束时,视图将不会更新,直到下一个布局周期,通常是退出当前范围时。因此,当您想要为约束设置动画时,可以在动画块之前更改常量,然后在动画块内调用layoutIfNeeded()这将告诉它为当前强制布局周期设置动画。

现在在你的例子中,你在那里调用layoutIfNeeded(),在那里什么也不做,然后改变2个常量,而不告诉布局动画,所以当范围退出时,UI将布局而没有动画。 / p>

在你的情况下,你想链接2个常量的变化,但你没有办法在第一个动画完成后改变第二个常量(80),如果你设置两个,两个常量都将被调整FIRST layoutIfNeeded()因此,动画的链接仍将与您省略的其他动画一起使用,只需在第一个动画块中添加它们,如果它们在第一个关键帧中,则第二个动画块在第二个动画块中,如果它们在第二个动画块中

最终编辑:

此外,您可以将当前应用于self.view的layoutIfNeeded()实际更改为其影响的视图,在本例中为iconVerticalConstraint,如果它是附加到另一个视图的视图,则表示附加了顶部在视图底部,如果你想要制作动画,你必须在它影响的每个视图上调用layoutIfNeeded()

答案 1 :(得分:2)

花了几个小时的时间来理解基本机制,我可以为所有发现自己处境的人提供我自己的答案。

layoutIfNeeded()告诉视图调整所有子视图的位置animateanimateKeyframes或您正在使用的所有其他替代方案,会告诉视图以您指定的方式执行动画块中的任何指令在animate函数中(默认情况下,使用options: .easeInOut

回到问题:问题在于animatelayoutIfNeeded被称为 异步 ,所以事实上,每一个动画块内的指令在layoutIfNeeded指令之前执行,使其无效。根据SeanLintern88的建议,解决方案不是将layoutIfNeeded指令放在动画块中,因为这样我们就会根据动画告诉视图 调整其子视图的位置选项 即可。这是关键点。

答案有点简单。将动画部分移动到子视图本身,特别是在您知道超级视图已经过&#34; autolayouted&#34; (例如viewDidAppear),以便已经定义并执行约束,现在可以更改它。在这一点上,代码应如下所示:

UIView.animateKeyframes(withDuration: 2, delay: 0, animations: {

    self.view.layoutIfNeeded()

    UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: 0.25, animations:{
        self.iconVerticalConstraint.constant -= 20
    })

    UIView.addKeyframe(withRelativeStartTime: 0.25, relativeDuration: 0.75, animations:{
        self.iconVerticalConstraint.constant -= 80
    })
})

因为想要为自动布局设置动画。

另一个选项(例如,如果您没有想要为子视图创建课程)是要更改 界限/中心 viewDidAppear函数中。这是因为,如果您在父视图中,您知道此时自动布局已经已注册但未执行,更准确地说,边界和中心已根据已更改自动布局。

如果您随后更改了它们,您将获得所需的动画(但记住:自动布局仍处于启用状态,因此您必须以反映最终位置的方式更改约束你的动画;如果你愿意,你可以在completion函数的animate部分那样做。