Home > Software design >  How to fix an off-centered stack view?
How to fix an off-centered stack view?

Time:05-27

Here I have a horizontal stack view with three subviews, however, it looks to be a bit off-centered as shown below.

enter image description here

I tried to:

  1. Constrain the stack view's centerXAnchor to be equal to the superview's centerXAnchor
  2. Set the stack view's alignment to center

How can I correctly align my stack view to the center of the superview?

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)
        ])
    }
}

CodePudding user response:

Tthe ivs do not have its translatesAutoresizingMaskIntoConstraints set to false. This makes its position fixed at (0, 0) within the views. The views are properly stretched by the stack view however, which is why a frame of (0, 0, 105, 118) leaves a lot of blank space on the right.

The alignment of a stack view describes how things are distributed perpendicular to the axis. In this case, the axis is horizontal, so alignment is about how things align along the height of the stack view, not the width as you may have assumed. It seems like you want .fill here - the "boxes" should fill the height of the stack view.

In any case, a simple solution is to position the ivs correctly within the views by adding constraints.

You should first set

iv.translatesAutoresizingMaskIntoConstraints = false

as you are about to add constraints. Then add these constraints:

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

I also don't see anything that determines the height of macroStackView. You should add a height constraint to macroStackView.

macroStackView.heightAnchor.constraint(equalToConstant: 118)

Alternatively, you could un-embed iv from the view, and just return iv directly.

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
}()

In this case, you would need to set the spacing of the stack view to achieve the spacing that you want.

  • Related