Xcode 当isHidden==true时,从左侧设置动画的UIStackView

Xcode 当isHidden==true时,从左侧设置动画的UIStackView,xcode,jquery-animate,uistackview,Xcode,Jquery Animate,Uistackview,我有一个包含许多排列子视图的视图。我希望它们都隐藏在viewDidLoad上,所以我去做了 public func setStackViewHidden(_ isHidden: Bool, animated: Bool) { stackView.arrangedSubviews.forEach ({ subview in if animated { UIView.animate(withDuration: 0.3) {

我有一个包含许多排列子视图的视图。我希望它们都隐藏在viewDidLoad上,所以我去做了

public func setStackViewHidden(_ isHidden: Bool, animated: Bool) {
    stackView.arrangedSubviews.forEach ({ subview in
        if animated {
            UIView.animate(withDuration: 0.3) {
                subview.isHidden = isHidden
            }
        } else {
            subview.isHidden = isHidden
        }
    })
}

这会隐藏整个stackView。当我使用相同的方法处理isHidden==false时。它从左侧开始设置动画,并向右扩展到正确的大小。为什么它不从正确的宽度到底部,而是从左到整个尺寸进行动画制作?

当显示/隐藏排列的子视图时,内置动画
UIStackView
可以很好地工作,但效果可能不是很好

堆栈视图的一个关键功能是排列其子视图。当然,通常还使用子视图的宽度和高度来确定堆栈视图的宽度和高度。因此,我们可以以未定义的状态结束,而动画——正如您所看到的——变得“不稳定”

尝试一下(所有代码,没有
@IBOutlets
@IBActions
,因此只需为此类分配一个视图控制器):


根据约束的设置方式,当所有视图都隐藏时,堆栈视图没有固有大小。因此,当子视图“取消隐藏”时,动画将在设置大小之前开始。尝试为第一个排列的子视图设置宽度约束,看看是否得到所需的结果。没有帮助,也许在我的stackview中覆盖intristicSize会有所帮助。
class StackShowHideViewController: UIViewController {

    // UIButton
    let btn: UIButton = {
        let v = UIButton()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.setTitle("Tap Me", for: .normal)
        v.backgroundColor = .red
        return v
    }()

    // vertical UIStackView
    let stackView: UIStackView = {
        let v = UIStackView()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.axis = .vertical
        v.alignment = .fill
        v.distribution = .fill
        v.spacing = 8
        return v
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        // add elements to view
        view.addSubview(btn)
        view.addSubview(stackView)

        NSLayoutConstraint.activate([

            // constrain button 20-pts from top, centered horizontally
            btn.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20.0),
            btn.centerXAnchor.constraint(equalTo: view.centerXAnchor),

            // constrain stack view 20-pts from bottom of button
            // centered horizontally
            // width constrained to 160-pts
            stackView.topAnchor.constraint(equalTo: btn.bottomAnchor, constant: 20.0),
            stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            stackView.widthAnchor.constraint(equalToConstant: 160.0),

        ])

        // add 5 UILabels to the stack view
        for i in 1...5 {
            let v = UILabel()
            v.backgroundColor = .yellow
            v.text = "This is Label \(i)"
            v.textAlignment = .center
            v.translatesAutoresizingMaskIntoConstraints = false
            // inititially hidden
            v.isHidden = true
            // add label to stack view
            stackView.addArrangedSubview(v)
            // constrain label widths to width of stack view
            v.widthAnchor.constraint(equalTo: stackView.widthAnchor).isActive = true
        }

        btn.addTarget(self, action: #selector(didTap(_:)), for: .touchUpInside)

    }

    public func setStackViewHidden(_ isHidden: Bool, animated: Bool) {
        stackView.arrangedSubviews.forEach ({ subview in
            if animated {
                UIView.animate(withDuration: 0.3) {
                    subview.isHidden = isHidden
                }
            } else {
                subview.isHidden = isHidden
            }
        })
    }

    @objc func didTap(_ sender: Any) {
        // if labels are hidden, show them
        // else, hide them
        var h = true
        if let v = stackView.arrangedSubviews.first {
            h = v.isHidden
        }
        setStackViewHidden(!h, animated: true)
    }

}