Here is how I understand how UIStackView works from Apple's document:
Quote:
Perpendicular to the stack view’s axis, its fitting size is equal to the size of the largest arranged view. 2. If I use alignment = .fill, all subview will be resized to the same size (stackview's size)
So I thought, if I created a simple vertical stack view containing 3 subview with different width, and I don't specify the stackview's width. UIKit will first do 1 then 2 above and resize everything using the largest subview, right?
I was wrong, the smallest subview is used. I don't understand why.
Here is code and the screenshot of the result.
Here is my code (I wrap it in swiftUI wrapper so you can see the canvas display directly):
import Foundation
import UIKit
import SwiftUI
import SnapKit
class TestViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
// Create the stack view with a vertical axis
let stackView = UIStackView()
stackView.axis = .vertical
stackView.backgroundColor = .gray
stackView.spacing = 20
stackView.alignment = .fill // Change this to .leading to see the effect
stackView.translatesAutoresizingMaskIntoConstraints = false
// Add the stack view to the view controller's view
view.addSubview(stackView)
// Constraints for the stack view
stackView.snp.makeConstraints { make in
make.top.equalTo(view)
make.left.equalTo(view)
}
// Create three views with different widths
let view1 = UIView()
view1.translatesAutoresizingMaskIntoConstraints = false
view1.backgroundColor = .red
view1.snp.makeConstraints { make in
make.width.equalTo(100)
make.height.equalTo(50)
}
let view2 = UIView()
view2.translatesAutoresizingMaskIntoConstraints = false
view2.backgroundColor = .green
view2.snp.makeConstraints { make in
make.width.equalTo(200)
make.height.equalTo(50)
}
let view3 = UIView()
view3.translatesAutoresizingMaskIntoConstraints = false
view3.backgroundColor = .blue
view3.snp.makeConstraints { make in
make.width.equalTo(300)
make.height.equalTo(50)
}
// Add views to the stack view
stackView.addArrangedSubview(view2)
stackView.addArrangedSubview(view1)
stackView.addArrangedSubview(view3)
}
}
struct TestViewControllerTestView : UIViewControllerRepresentable {
typealias UIViewControllerType = TestViewController
func makeUIViewController(context: Context) -> TestViewController {
return TestViewController()
}
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
}
}
struct TestViewController_Preview : PreviewProvider {
static var previews: some View {
TestViewControllerTestView()
}
}
UIStackView
can take a little experimentation to fully understand how it lays out views.
In general, we use constraints to set the size of the stack view and then allow it to control the subviews layout.
In the case of your vertical axis stack view, with no width or height constraint...
let stackView = UIStackView()
stackView.axis = .vertical
stackView.spacing = 20
stackView.alignment = .fill
If we do not explicitly set width of any arranged subviews, auto-layout will use the view with the greatest .intrinsicContentSize.width to set the stack view width, and then fill the other views.
If we change your code to this:
let v1 = UILabel()
v1.text = "Short text"
v1.backgroundColor = UIColor(red: 1.0, green: 0.6, blue: 0.6, alpha: 1.0)
v1.snp.makeConstraints { make in
make.height.equalTo(50)
}
let v2 = UILabel()
v2.text = "Medium length text"
v2.backgroundColor = .green
v2.snp.makeConstraints { make in
make.height.equalTo(50)
}
let v3 = UILabel()
v3.text = "Longest amount of text in the label"
v3.backgroundColor = .cyan
v3.snp.makeConstraints { make in
make.height.equalTo(50)
}
stackView.addArrangedSubview(v2)
stackView.addArrangedSubview(v3)
stackView.addArrangedSubview(v1)
we get this output:
If we set the width of v1 (the shortest text) to, let's say, 120
:
v1.snp.makeConstraints { make in
make.width.equalTo(120)
make.height.equalTo(50)
}
we end up with this:
If we set it to 320
:
v1.snp.makeConstraints { make in
make.width.equalTo(320)
make.height.equalTo(50)
}
we get this:
In both cases, we're explicitly setting the width of one of the subviews, but NOT setting a width on either of the other two.
As I mentioned in my comment, your code is saying:
"Hey auto-layout... make the 3 views different widths but also make them the same widths."
Of course, auto-layout cannot do that. You should see plenty of error messages in the debug console. In this specific case, auto-layout is using the narrowest width constraint -- but, in all error cases, the actual behavior is undefined.
If you want all 3 views to stretch the width of the stack view, you need to either:
A - only set the width constraint on the widest view, or
B - constrain the width of the stack view, but NOT any of the arranged subviews.
Which approach to take will depend on various factors which are not known here, as it's unlikely that red, green, blue "bars" is your actual goal.