1

I've a stack view with label, image view and button inside it. This stack view is configured for vertical axis with distribution and alignment set to fill. The label is not aligning to centre of the view as the button does. Why is this weird behaviour seen and how to fix this?

enter image description here

class ViewController: UIViewController {

    let flowerLabel: UILabel = {
        let label = UILabel()
        label.text = "Flowers"
        label.font = UIFont.systemFont(ofSize: 25)
        label.numberOfLines = 0
        return label
    }()
    
    var flowerImageView: UIImageView! = {
        let flowerImageView = UIImageView(image: UIImage(named: "flowers"))
        flowerImageView.contentMode = .scaleAspectFit
        flowerImageView.setContentHuggingPriority(.defaultLow - 1, for: .vertical)
        flowerImageView.setContentCompressionResistancePriority(.defaultHigh - 1, for: .vertical)
       return flowerImageView
    }()
    
    var editButton = UIButton.customButton(title: "Edit", color: .blue, fontSize: 25)
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        
    }
    
    lazy var stack :UIStackView = {
        let stack = UIStackView(arrangedSubviews: [flowerLabel,flowerImageView,editButton])
        stack.translatesAutoresizingMaskIntoConstraints = false
        stack.axis = .vertical
        stack.spacing = 8
        stack.alignment =  .fill
        stack.distribution = .fill
        return stack
    }()
    override func viewDidLoad() {
        super.viewDidLoad()
       view.addSubview(stack)
        view.backgroundColor = .white
        NSLayoutConstraint.activate([
            stack.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 8),
            stack.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            stack.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            stack.trailingAnchor.constraint(equalTo: view.trailingAnchor)
        ])
    }
}

private extension UIButton {
    static func customButton(title: String, color: UIColor,
                             fontSize: CGFloat) -> UIButton {
        let button = UIButton(type: .custom)
        button.setTitle(title, for: .normal)
        button.setTitleColor(color, for: .normal)
        button.titleLabel?.font = UIFont.systemFont(ofSize:
            fontSize)
        return button
    }
}
Sachin
  • 13
  • 4

2 Answers2

1

Here is the fix of your class & stack.distribution = .fillEqually, other then your code is same, also I have attach screenshot for reference add background color to see stackview inner objects.enter image description here

yes @sweeper is right I have change label alignment and

class ViewController: UIViewController {

    let flowerLabel: UILabel = {
        let label = UILabel()
        label.text = "Flowers"
        label.font = UIFont.systemFont(ofSize: 25)
        label.numberOfLines = 0
        label.backgroundColor = .purple
        label.textAlignment = .center
        return label
    }()
    
    var flowerImageView: UIImageView! = {
        let flowerImageView = UIImageView(image: UIImage(named: "premiumPop"))
        flowerImageView.contentMode = .scaleAspectFit
        flowerImageView.setContentHuggingPriority(.defaultLow - 1, for: .vertical)
        flowerImageView.setContentCompressionResistancePriority(.defaultHigh - 1, for: .vertical)
        flowerImageView.backgroundColor = .blue
       return flowerImageView
    }()
    
    var editButton = UIButton.customButton(title: "Edit", color: .blue, fontSize: 25)
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        
    }
    
    lazy var stack :UIStackView = {
        let stack = UIStackView(arrangedSubviews: [flowerLabel,flowerImageView,editButton])
        stack.translatesAutoresizingMaskIntoConstraints = false
        stack.axis = .vertical
        stack.spacing = 8
        stack.alignment =  .fill
        stack.distribution = .fillEqually
        return stack
    }()
    override func viewDidLoad() {
        super.viewDidLoad()
        
        stack.backgroundColor = .red
       view.addSubview(stack)
        view.backgroundColor = .white
        NSLayoutConstraint.activate([
            stack.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 8),
            stack.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            stack.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            stack.trailingAnchor.constraint(equalTo: view.trailingAnchor)
        ])
    }
}

private extension UIButton {
    static func customButton(title: String, color: UIColor,
                             fontSize: CGFloat) -> UIButton {
        let button = UIButton(type: .custom)
        button.setTitle(title, for: .normal)
        button.setTitleColor(color, for: .normal)
        button.titleLabel?.font = UIFont.systemFont(ofSize:
            fontSize)
        button.backgroundColor = .brown
        return button
    }
}
Chandaboy
  • 1,302
  • 5
  • 10
0

You have set alignment of the stack view to .fill. This means that the arranged views will fill the entire width of the stack view.

When a button is "stretched" to fit the width, its text is centre-aligned. This is because buttons place their titleLabels in the centre of the button by default.

When a UILabel is stretched to fit the width however, where the text will appear in the label depends on the label's textAlignment. By default, this is .natural, which in a LTR language, means .left.

So you should do:

label.textAlignment = .center

Alternatively, set the stack view's alignment to .center so the label doesn't get stretched.

stack.alignment =  .center
Sweeper
  • 213,210
  • 22
  • 193
  • 313