如何修复偏离中心的堆栈视图?

How to fix an off-centered stack view?

这里我有一个包含三个子视图的水平堆栈视图,但是,它看起来有点偏离中心,如下所示。

我试过:

  1. 约束堆栈视图的 centerXAnchor 等于父视图的 centerXAnchor
  2. 将堆栈视图的对齐方式设置为居中

如何正确地将堆栈视图对齐到父视图的中心?

class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        view.addSubview(macroStackView)
        macroStackView.translatesAutoresizingMaskIntoConstraints = false
        constrainView()
    }
    
    lazy var carbView: UIView = {
        var view = UIView()
        
        var iv = UIImageView(image: Image.planViewBg)
        iv.frame = CGRect(x: 0, y: 0, width: 105, height: 118)
        iv.contentMode = .scaleAspectFit
        
        var label1 = UILabel()
        label1.text = "31%"
        label1.textAlignment = .center
        
        var label2 = UILabel()
        label2.text = "30g"
        label2.textAlignment = .center
        
        var label3 = UILabel()
        label3.text = "Carbs"
        label3.textAlignment = .center
        
        var labelStack = UIStackView(arrangedSubviews: [label1, label2, label3])
        labelStack.axis = .vertical
        labelStack.translatesAutoresizingMaskIntoConstraints = false
        
        view.addSubview(iv)
        iv.addSubview(labelStack)
        
        NSLayoutConstraint.activate([
            labelStack.centerXAnchor.constraint(equalTo: iv.centerXAnchor),
            labelStack.centerYAnchor.constraint(equalTo: iv.centerYAnchor),
        ])
        
        return view
    }()
    
    lazy var proteinView: UIView = {
        var view = UIView()
        
        var iv = UIImageView(image: Image.planViewBg)
        iv.frame = CGRect(x: 0, y: 0, width: 105, height: 118)
        iv.contentMode = .scaleAspectFit
        
        var label1 = UILabel()
        label1.text = "23%"
        label1.textAlignment = .center
        
        var label2 = UILabel()
        label2.text = "23g"
        label2.textAlignment = .center
        
        var label3 = UILabel()
        label3.text = "Protein"
        label3.textAlignment = .center
        
        var labelStack = UIStackView(arrangedSubviews: [label1, label2, label3])
        labelStack.axis = .vertical
        labelStack.translatesAutoresizingMaskIntoConstraints = false
        
        view.addSubview(iv)
        iv.addSubview(labelStack)
        
        NSLayoutConstraint.activate([
            labelStack.centerXAnchor.constraint(equalTo: iv.centerXAnchor),
            labelStack.centerYAnchor.constraint(equalTo: iv.centerYAnchor),
        ])
        
        return view
    }()
    
    lazy var fatView: UIView = {
        var view = UIView()
        
        var iv = UIImageView(image: Image.planViewBg)
        iv.frame = CGRect(x: 0, y: 0, width: 105, height: 118)
        iv.contentMode = .scaleAspectFit
        
        var label1 = UILabel()
        label1.text = "46%"
        label1.textAlignment = .center
        
        var label2 = UILabel()
        label2.text = "20g"
        label2.textAlignment = .center
        
        var label3 = UILabel()
        label3.text = "Fat"
        label3.textAlignment = .center
        
        var labelStack = UIStackView(arrangedSubviews: [label1, label2, label3])
        labelStack.axis = .vertical
        labelStack.translatesAutoresizingMaskIntoConstraints = false
        
        view.addSubview(iv)
        iv.addSubview(labelStack)
        
        NSLayoutConstraint.activate([
            labelStack.centerXAnchor.constraint(equalTo: iv.centerXAnchor),
            labelStack.centerYAnchor.constraint(equalTo: iv.centerYAnchor),
        ])
        
        return view
    }()
    
    lazy var macroStackView: UIStackView = {
        var stack = UIStackView(arrangedSubviews: [carbView, proteinView, fatView])
        stack.axis = .horizontal
        stack.distribution = .fillEqually
        stack.alignment = .center
        
        return stack
    }()
    
    func constrainView() {
        NSLayoutConstraint.activate([
            macroStackView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            macroStackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 30),
            macroStackView.widthAnchor.constraint(equalTo: view.widthAnchor,
                                                  multiplier: 0.9)
        ])
    }
}

Tiv 没有将其 translatesAutoresizingMaskIntoConstraints 设置为 false。这使得它的位置固定在 views 内的 (0, 0)。然而,view 被堆栈视图适当地拉伸,这就是为什么 (0, 0, 105, 118) 的帧在右边留下很多空白 space。

堆栈视图的对齐描述了事物如何垂直分布到轴。在这种情况下,轴是水平的,因此 alignment 是关于事物如何沿堆栈视图的高度对齐,而不是您可能假设的 width。看起来你想要 .fill 这里 - “框”应该填满堆栈视图的高度。

无论如何,一个简单的解决方案是通过添加约束将 iv 正确定位在 view 中。

你应该先设置

iv.translatesAutoresizingMaskIntoConstraints = false

您将要添加约束。然后添加这些约束:

iv.widthAnchor.constraint(equalToConstant: 105),
iv.heightAnchor.constraint(equalToConstant: 118),
iv.centerXAnchor.constraint(equalTo: view.centerXAnchor),
iv.centerYAnchor.constraint(equalTo: view.centerYAnchor),

我也没有看到任何决定 macroStackView 高度的东西。您应该向 macroStackView.

添加高度限制
macroStackView.heightAnchor.constraint(equalToConstant: 118)

或者,您可以从 view un-embed iv,直接 return iv

lazy var carbView: UIView = {
    var iv = UIImageView(image: Image.planViewBg)
    iv.translatesAutoresizingMaskIntoConstraints = false
    iv.contentMode = .scaleAspectFit
    
    var label1 = ...
    var label2 = ...
    var label3 = ...
    
    var labelStack = UIStackView(arrangedSubviews: [label1, label2, label3])
    ...
    iv.addSubview(labelStack)
    
    ...
    
    return iv
}()

在这种情况下,您需要设置堆栈视图的 spacing 以获得您想要的间距。