I am trying to create a grid of 3xInfinity items in Swift with same size items. The items are square buttons that can be aligned up to 3 items wide and infinite on the Y axis. I have created a function that takes a UIStackView with axis vertical and every 3 items I create a new UIStackView with horizontal axis inside of the other one. This works well but only when all the buttons are multiple of 3. Whenever they are not the row makes the buttons fill all the available space.
I would like to know how to create a grid of 3xInfinity items in Swift so that the buttons are always evenly spaced, even if there is an odd number of buttons.
How currently is displayed
How it should be displayed
Code
let stackview = UIStackView()
contentView.addSubview(stackview)
stackview.axis = .vertical
stackview.alignment = .fill
stackview.distribution = .fill
stackview.spacing = 10
for _ in 0...feedButtonRows {
let hstack = UIStackView()
hstack.axis = .horizontal
hstack.alignment = .center
hstack.distribution = .fillEqually
hstack.spacing = 16
hstack.translatesAutoresizingMaskIntoConstraints = false
hstack.widthAnchor.constraint(equalToConstant: stackview.frame.size.width).isActive = true
for _ in 0..<3 {
if(count == buttons.count){
break
}
let button = FoodButtonComponent(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
hstack.addArrangedSubview(button)
button.widthAnchor.constraint(equalToConstant: 100).isActive = true
button.heightAnchor.constraint(equalToConstant: 100).isActive = true
button.setup(with: buttons[count])
count+=1
}
stackview.addArrangedSubview(hstack)
}
2
Answers
You can’t have
distribution = .fillEqually
and then at the same time expect that constraining your view size will have any effect usingwidthAnchor.constraint(equalToConstant: 100).isActive = true
. You must be getting some runtime warnings about constraints being in conflict.Also when using constraints remember to disable auto-translation of resizing mask into constraints for in-code-generated views.
Anyway, you can still do what you want by simply adding an empty view instead of breaking when index goes past 3. You can actually create a standalone method to layout your views. Check out this implementation:
I can now use this in multiple ways. For instance this will fill the size of a parent:
Or you can have some implicit size defined by views inside the stack view
Or you can have something in-between where width is constrained by parent but height is not:
If you set your vertical stack view’s
.alignment = .leading
and do not give the horizontal "row" stack views width constraints, the buttons will be left-aligned and won’t get stretched.You can also simplify your code a bit like this:
Note that when views (labels, buttons, etc) are added as
arrangedSubviews
of a stack view, UIKit automatically sets.translatesAutoresizingMaskIntoConstraints = false
— so no need to explicitly set that.Here’s a complete example – change the
numButtons
at the top to see the layouts:When run: