1

I have created UIStackView programmatically and added 2 views that have 2 child views each. Here are my sample codes:

let sv = UIStackView()
sv.axis = .horizontal
sv.alignment = .center
sv.spacing = Config.Dimensions.horizontalSpacing
sv.distribution = .equalCentering
sv.translatesAutoresizingMaskIntoConstraints = false

let viewCountStudent = UIView()
viewCountStudent.addSubview(studentCount)
viewCountStudent.addSubview(labelStudent)
studentCount.topAnchor.constraint(equalTo: viewCountStudent.topAnchor).isActive = true
studentCount.leftAnchor.constraint(equalTo: viewCountStudent.leftAnchor).isActive = true
studentCount.bottomAnchor.constraint(equalTo: viewCountStudent.bottomAnchor).isActive = true
labelStudent.topAnchor.constraint(equalTo: viewCountStudent.topAnchor).isActive = true
labelStudent.leftAnchor.constraint(equalTo: studentCount.rightAnchor, constant: 8.0).isActive = true
labelStudent.rightAnchor.constraint(equalTo: viewCountStudent.rightAnchor).isActive = true
labelStudent.bottomAnchor.constraint(equalTo: viewCountStudent.bottomAnchor).isActive = true

let viewCountLesson = UIView()
viewCountLesson.addSubview(lessonCount)
viewCountLesson.addSubview(labelLesson)
lessonCount.leftAnchor.constraint(equalTo: viewCountLesson.leftAnchor).isActive = true
lessonCount.topAnchor.constraint(equalTo: viewCountLesson.topAnchor).isActive = true
lessonCount.bottomAnchor.constraint(equalTo: viewCountLesson.bottomAnchor).isActive = true
labelLesson.leftAnchor.constraint(equalTo: lessonCount.rightAnchor, constant: 8.0).isActive = true
labelLesson.rightAnchor.constraint(equalTo: viewCountLesson.rightAnchor).isActive = true
labelLesson.topAnchor.constraint(equalTo: viewCountLesson.topAnchor).isActive = true
labelLesson.bottomAnchor.constraint(equalTo: viewCountLesson.bottomAnchor).isActive = true

sv.addArrangedSubview(viewCountLesson)
sv.addArrangedSubview(viewCountStudent)

sv.topAnchor.constraint(equalTo: divider.bottomAnchor, constant: 8.0).isActive = true
sv.leftAnchor.constraint(equalTo: divider.leftAnchor, constant: 16.0).isActive = true
sv.rightAnchor.constraint(equalTo: divider.rightAnchor, constant: -16.0).isActive = true
sv.bottomAnchor.constraint(equalTo: guide.bottomAnchor, constant: -8.0).isActive = true

addSubview(sv)

The layout it gives is like this:

The layout it gives is like this

Above is the horizontal bar and below is the StackView. I wonder why the gap in between 2 views are not equally distributing by stack view. I am trying to center them with spacing distributing equally. Any idea?

codelearner
  • 1,354
  • 1
  • 16
  • 32
  • It's not clear what are you expecting to see? You're getting the result I'd expect that code to produce but it sound like you're expecting something different than what `equalCentering` does. – Craig Siemens Apr 16 '19 at 16:41
  • I was expecting `equalCentering` to minimize the wide gap in between and distribute this gap in the beginning and end of 2 views. – codelearner Apr 16 '19 at 17:01

1 Answers1

5

This may help you understand...

Each "row" of three green labels is a Horizontal Stack View with Spacing: 8 and Distribution set to:

  • Fill
  • Fill Equally
  • Fill Proportionally
  • Equal Centering
  • Equal Spacing

enter image description here

As you can see, with Distribution: Equal Centering, the stack view arranges its subviews so their centers are equally spaced.

What you probably want is equal spacing on the sides and in-between:

enter image description here

To get that layout, use Distribution: Fill and add an empty "spacer" view in the stack, so you have:

spacer1 - viewCountLesson - spacer2 - viewCountStudent - spacer3

then set spacer2 width equal to spacer1 and spacer3 width equal to spacer1.

Here is the code used to create that:

class NewStackViewController: UIViewController {

    let studentCount: UILabel = {
        let v = UILabel()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.backgroundColor = .yellow
        v.text = "2"
        return v
    }()

    let lessonCount: UILabel = {
        let v = UILabel()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.backgroundColor = .yellow
        v.text = "1"
        return v
    }()

    let labelStudent: UILabel = {
        let v = UILabel()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.backgroundColor = .cyan
        v.text = "Students"
        return v
    }()

    let labelLesson: UILabel = {
        let v = UILabel()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.backgroundColor = .cyan
        v.text = "Lesson"
        return v
    }()

    let divider: UIView = {
        let v = UIView()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.backgroundColor = .gray
        return v
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        view.addSubview(divider)
        NSLayoutConstraint.activate([
            divider.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 20.0),
            divider.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -20.0),
            divider.heightAnchor.constraint(equalToConstant: 2.0),
            divider.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: 0.0),
            ])

        let sv = UIStackView()
        sv.axis = .horizontal
        sv.alignment = .fill
        sv.spacing = 0 //Config.Dimensions.horizontalSpacing
        sv.distribution = .fill
        sv.translatesAutoresizingMaskIntoConstraints = false

        view.addSubview(sv)
        NSLayoutConstraint.activate([
            sv.leadingAnchor.constraint(equalTo: divider.leadingAnchor, constant: 16.0),
            sv.trailingAnchor.constraint(equalTo: divider.trailingAnchor, constant: -16.0),
            sv.topAnchor.constraint(equalTo: divider.bottomAnchor, constant: 8.0),
            ])

        let viewCountStudent = UIView()
        viewCountStudent.addSubview(studentCount)
        viewCountStudent.addSubview(labelStudent)
        studentCount.topAnchor.constraint(equalTo: viewCountStudent.topAnchor).isActive = true
        studentCount.leftAnchor.constraint(equalTo: viewCountStudent.leftAnchor).isActive = true
        studentCount.bottomAnchor.constraint(equalTo: viewCountStudent.bottomAnchor).isActive = true
        labelStudent.topAnchor.constraint(equalTo: viewCountStudent.topAnchor).isActive = true
        labelStudent.leftAnchor.constraint(equalTo: studentCount.rightAnchor, constant: 8.0).isActive = true
        labelStudent.rightAnchor.constraint(equalTo: viewCountStudent.rightAnchor).isActive = true
        labelStudent.bottomAnchor.constraint(equalTo: viewCountStudent.bottomAnchor).isActive = true

        let viewCountLesson = UIView()
        viewCountLesson.addSubview(lessonCount)
        viewCountLesson.addSubview(labelLesson)
        lessonCount.leftAnchor.constraint(equalTo: viewCountLesson.leftAnchor).isActive = true
        lessonCount.topAnchor.constraint(equalTo: viewCountLesson.topAnchor).isActive = true
        lessonCount.bottomAnchor.constraint(equalTo: viewCountLesson.bottomAnchor).isActive = true
        labelLesson.leftAnchor.constraint(equalTo: lessonCount.rightAnchor, constant: 8.0).isActive = true
        labelLesson.rightAnchor.constraint(equalTo: viewCountLesson.rightAnchor).isActive = true
        labelLesson.topAnchor.constraint(equalTo: viewCountLesson.topAnchor).isActive = true
        labelLesson.bottomAnchor.constraint(equalTo: viewCountLesson.bottomAnchor).isActive = true

        let sp1 = spacerView()
        let sp2 = spacerView()
        let sp3 = spacerView()

        sv.addArrangedSubview(sp1)
        sv.addArrangedSubview(viewCountLesson)
        sv.addArrangedSubview(sp2)
        sv.addArrangedSubview(viewCountStudent)
        sv.addArrangedSubview(sp3)

        NSLayoutConstraint.activate([
            sp2.widthAnchor.constraint(equalTo: sp1.widthAnchor, multiplier: 1.0),
            sp3.widthAnchor.constraint(equalTo: sp1.widthAnchor, multiplier: 1.0),
            ])

        [sp1, sp2, sp3, viewCountLesson, viewCountStudent, studentCount, labelStudent, lessonCount, labelLesson].forEach {
            // set borderWidth to 1 to add borders to labels and views so we can see them
            $0.layer.borderWidth = 0
            $0.layer.borderColor = UIColor.lightGray.cgColor
            // un-comment next line to set backgrounds to clear
            //$0.backgroundColor = .clear
        }

    }

    func spacerView() -> UIView {

        let v = UIView()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.backgroundColor = .green
        return v

    }

}
DonMag
  • 69,424
  • 5
  • 50
  • 86