将 UIStackView 视图动画化到中心

Posted

技术标签:

【中文标题】将 UIStackView 视图动画化到中心【英文标题】:Animate UIStackView views into centre 【发布时间】:2020-10-06 18:47:39 【问题描述】:

UIStackView 中有 5 个按钮。其中之一是居中的。按住中心按钮后,我想将所有其他按钮设置为动画并隐藏它们。然后,当我释放中心按钮时,我想让它们从中心返回到原来的位置。

目前我的代码如下所示:

private func transform(views: [UIView], toIdentity isIdentity: Bool) 
   UIView.animate(withDuration: 0.3) 
        for view in views 
            if isIdentity 
                view.transform = .identity
             else 
                view.center.x = self.view.center.x
            
            
            view.isHidden = !isIdentity
        
    

这可行,但是当isIdentity 为false 时,按钮都会跳到父视图之外,然后移到中心。

我也尝试将这段代码放在动画块中。这避免了奇怪的跳跃问题,但它们比中心移动得更远。

let delta = self.view.center.x - view.center.x
view.transform = isIdentity ? .identity : view.transform.translatedBy(x: delta, y: 0)

为什么会这样,我该如何解决?

【问题讨论】:

这可能不是正确的方法...您在水平行中有 5 个按钮?当你点击并按住中间按钮时,你想让其他 4 个“滑动到中间”并隐藏在中间按钮后面? 是但不隐藏需要可见的中心按钮。其他 4 个需要隐藏。如果这不是正确的方法,那是什么? 在尝试将按钮嵌入堆栈视图时,您会遇到一些问题。您是在 Storyboard 中还是通过代码进行布局?按钮的宽度都一样吗? 【参考方案1】:

有几种方法可以做到这一点,但这里有一种使用堆栈视图的方法。

假设我们已经在 Storyboard 5 按钮的堆栈视图中布置了这些属性:

Axis: Horizontal
Alignment: Fill
Distribution: Fill Equally
Spacing: 8

(间距可根据需要更改)

试试这个代码:

class ButtonAnimViewController: UIViewController 
    
    @IBOutlet var buttonsStackView: UIStackView!
    
    override func viewDidLoad() 
        super.viewDidLoad()

        // we want the outer buttons to "slide in behind" the center button
        //  so we need to custom set their z-orders
        buttonsStackView.arrangedSubviews[0].layer.zPosition = 0
        buttonsStackView.arrangedSubviews[1].layer.zPosition = 1
        buttonsStackView.arrangedSubviews[2].layer.zPosition = 4
        buttonsStackView.arrangedSubviews[3].layer.zPosition = 3
        buttonsStackView.arrangedSubviews[4].layer.zPosition = 2
        
    
    
    @IBAction func doCollapse(_ sender: Any) 
        // verbose for clarity
        let btn1 = buttonsStackView.arrangedSubviews[0]
        let btn2 = buttonsStackView.arrangedSubviews[1]
        let btn3 = buttonsStackView.arrangedSubviews[2]
        let btn4 = buttonsStackView.arrangedSubviews[3]
        let btn5 = buttonsStackView.arrangedSubviews[4]
        
        // calculate translationX distance for each button to the center button
        let xDistance1 = btn3.center.x - btn1.center.x
        let xDistance2 = btn3.center.x - btn2.center.x
        
        let xDistance4 = btn3.center.x - btn4.center.x
        let xDistance5 = btn3.center.x - btn5.center.x
        
        // animate the transforms
        UIView.animate(withDuration: 0.3) 
            btn1.transform = CGAffineTransform(translationX: xDistance1, y: 0.0)
            btn2.transform = CGAffineTransform(translationX: xDistance2, y: 0.0)
            btn4.transform = CGAffineTransform(translationX: xDistance4, y: 0.0)
            btn5.transform = CGAffineTransform(translationX: xDistance5, y: 0.0)
        
    
    
    @IBAction func doExpand(_ sender: Any) 
        // verbose for clarity
        let btn1 = buttonsStackView.arrangedSubviews[0]
        let btn2 = buttonsStackView.arrangedSubviews[1]
        let btn4 = buttonsStackView.arrangedSubviews[3]
        let btn5 = buttonsStackView.arrangedSubviews[4]

        // animate transforms back to identity
        UIView.animate(withDuration: 0.3) 
            btn1.transform = .identity
            btn2.transform = .identity
            btn4.transform = .identity
            btn5.transform = .identity
        
        
    
    


编辑

作为参考,UIStackView 的标准行为(在许多情况下很有用)是当您隐藏排列的子视图时发生的情况。

当子视图保留时,它的框架从堆栈视图的排列中移除

因此,例如,如果您在这样的子视图中有两个视图,前导/尾随 == 40 和分布 == 均等填充:

然后你设置viewB.isHidden = true,结果将是:

如你所见,如果你想让视图不可见,但让它继续影响布局,你需要设置它的.alpha = 0 而不是隐藏它。

【讨论】:

zPosition 的好地方,但我认为这基本上就是我所拥有的。除了我的堆栈视图是通过情节提要布局的,它的对齐是中心,分布是填充。视图的宽度不同。 这行得通。我意识到这是因为您的代码中没有设置isHidden。如果我使用我的原始代码并删除 isHidden 属性的设置,它也可以工作。现在我刚刚将其移至完成块并改为设置 alpha。奇怪! @Tometoyou - 我添加了一点说明...请参阅我的答案底部的 编辑【参考方案2】:

您确定您的堆栈视图居中吗?我不明白为什么翻译代码不起作用。也许尝试从 centerView 而不是实际视图创建 Delta。注意:第一个动画中的问题是它正在更新中心,然后尝试使用无效的变换撤消该更新,它需要将帧更新回原始位置。

let delta = centerButton.center.x - view.center.x
view.transform = isIdentity ? .identity : view.transform.translatedBy(x: delta, y: 0)

【讨论】:

我也试过这个。实际上我发现这是因为 isHidden 设置在动画块中,所以它变得很奇怪。如果我将其移至完成块并在动画块内设置视图的 alpha 则很好!

以上是关于将 UIStackView 视图动画化到中心的主要内容,如果未能解决你的问题,请参考以下文章

自定义“添加”视图动画到 uistackview

如何在 UIStackView (iOS 9)的中心周围均匀分布两个子视图?

UIStackView,通过调整动画大小隐藏子视图

UIStackView 隐藏视图动画

UIStackView 没有将自身对齐到中心?对象-c

UIStackView 子视图仅在 isHidden 设置为 false 时才动画