AutoLayout 约束动画从错误的点缩放

Posted

技术标签:

【中文标题】AutoLayout 约束动画从错误的点缩放【英文标题】:AutoLayout constraint animation scales from wrong point 【发布时间】:2018-08-22 14:29:39 【问题描述】:

我正在尝试使用自动布局约束实现动画,并与 UIStackViews 一起工作。我遇到了一个动画。

我有一个嵌套在 UIView(橙色)中的 UIImageView(绿色),它又由 UIStackView 处理。当只有这些元素可见时,视图应该缩小。我正在尝试更改以下约束:

通常宽度和高度为 64,前导/尾随/顶部/底部约束为 10,我使用以下方法将它们分别更改为 46 和 7:

private func setImageShrunk() 
    imageWidth.constant = 46
    imageHeight.constant = 46
    imageTopMargin.constant = 7
    imageBottomMargin.constant = 7
    imageRightMargin.constant = 7
    imageLeftMargin.constant = 7

这个函数被另一个函数调用:

func shrinkImage(animated: Bool) 
    if(animated) 
        UIView.animate(withDuration: 1) 
            self.setImageShrunk()
            self.layoutIfNeeded()
        
     else 
        setImageShrunk()
    

在没有动画的情况下缩小图像会返回预期的结果,当然是突然的。但是,尝试对其进行动画处理,我得到以下结果:

我希望视图向左上角缩小,而不是从中心缩小。它不应该“先移动,后扩展”。有谁知道如何在 ios 11 中使用 AutoLayout 来实现这一点?

【问题讨论】:

如果您使用 0 而不是 7 作为顶部和左侧边距会发生什么? 抱歉,我认为页边距适用于橙色视图及其超级视图。您可以将橙色视图的顶部和左侧的约束设置为其超级视图,并在 shrinkImage 方法中将它们设置为 0 @AliMoazenzadeh 顶部和左侧的约束已经设置为 0,这就是为什么发生这种情况让我如此困惑的原因 您可以尝试将顶部和左侧边距限制优先级设置为低于 1000 的值吗?以 990 为例 @AliMoazenzadeh 我已经尝试过使用优先级,结果是even weirder... 【参考方案1】:

所以我弄清楚了尝试制作一个小项目来重新创建错误时发生了什么。

在第二个项目中,设置是相同的,但动画效果符合我的预期。经过一番思考,我意识到在这个新项目中,我将动画从 UIViewController 包含 UIStackView 调用。

在最初的问题中,我没有提到 我正在从扩展 UIStackView 的类中调用动画,因此视图本身会被动画化。即使它从外部引用了约束,视图也不知道它的环境和嵌入视图。这就是为什么它只是跳跃然后缩放。

我通过将要完成的动画传输到包含UIStackViewUIViewController 来解决此问题。

【讨论】:

谢谢,在我的情况下,我只需要在父视图上调用 layoutIfNeeded(),而不是动画视图本身【参考方案2】:

如果您设置了 width 和 height ,则不需要 right 和 bottom 约束,因为当您将 height 从 64 更改为 46 时,底部距离应为 28 ( 10 + 18 ),并且由于您将其设置为 7 ,那么就有冲突了,对的也一样,要么删除它们,要么降低它们的优先级

顺便说一句,所有这些只有在橙色视图有其他宽度和高度约束时才有效

【讨论】:

【参考方案3】:

你设置了 UIView 的锚点了吗?它现在在中心点收缩,而不是左上角。您还应该创建一个自定义动画来定义两个动画并将它们链接在一起。您可以在网上找到很多解决方案,但这里是一个示例。是针对 sprite kit 的,但是思路是一样的。

        #if os(iOS)
    let transform:SKAction = SKAction.move(to: CGPoint(x: -40, y: -45), duration: 0.3)
    let rotateAction:SKAction = SKAction.rotate(toAngle: degree(-20), duration: 0.3)
        #else
        let transform:SKAction = SKAction.move(to: CGPoint(x: -40, y: 60), duration: 0.3)
        let rotateAction:SKAction = SKAction.rotate(toAngle: degree(-20), duration: 0.3)
        #endif
    let endAction:SKAction = SKAction.perform(#selector(GameScene.animateToTableEnded), onTarget: self)
    let combineAction:SKAction = SKAction.sequence([SKAction.group([transform, rotateAction]), endAction])

实际上这里的分组创造了魔力。

这是来自 Apple 文档的核心动画解决方案。

let fadeOut = CABasicAnimation(keyPath: "opacity")
fadeOut.fromValue = 1
fadeOut.toValue = 0
fadeOut.duration = 1

let expandScale = CABasicAnimation()
expandScale.keyPath = "transform"
expandScale.valueFunction = CAValueFunction(name: kCAValueFunctionScale)
expandScale.fromValue = [1, 1, 1]
expandScale.toValue = [3, 3, 3]

let fadeAndScale = CAAnimationGroup()
fadeAndScale.animations = [fadeOut, expandScale]
fadeAndScale.duration = 1

【讨论】:

以上是关于AutoLayout 约束动画从错误的点缩放的主要内容,如果未能解决你的问题,请参考以下文章

同时使用不同的动画曲线为 AutoLayout 约束设置动画

AutoLayout常量更改不动画

Autolayout约束动画化-Animating Autolayout Constraints

iOS AutoLayout的用法

代码适配(Autolayout)

基于Autolayout的动画