The UIStackView doesn't have the correct .fill distribution if subviews are UIButtons created with UIButton.Configuration.
In the example below I created two UIStackView with subviews of UIButton type. The two stackviews are the same, but subview UIButtons are created in different way
let stackView1 = UIStackView()
stackView1.translatesAutoresizingMaskIntoConstraints = false
stackView1.distribution = .fill
If UIButtons are created with regular constructors,
let button = UIButton()
the .fill distribution works as expected (look at the top stack view in the attached image)
If UIButtons are created with Configuration passed to the constructor
var configuration = UIButton.Configuration.plain()
let button = UIButton(configuration: configuration)
the parent UIStackView doesn't provide correct fill distribution (bottom stack view in the attached image).
How to make UIStackView provide the correct distribution for UIButtons created with Configuration?
env: XCode 15.4, Simulator iOS 17.5
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let stackView1 = UIStackView()
stackView1.translatesAutoresizingMaskIntoConstraints = false
stackView1.distribution = .fill
stackView1.alignment = .fill
let stackView2 = UIStackView()
stackView2.translatesAutoresizingMaskIntoConstraints = false
stackView2.distribution = .fill
stackView2.alignment = .fill
view.addSubview(stackView1)
view.centerXAnchor.constraint(equalTo: stackView1.centerXAnchor).isActive = true
view.centerYAnchor.constraint(equalTo: stackView1.centerYAnchor).isActive = true
view.addSubview(stackView2)
view.centerXAnchor.constraint(equalTo: stackView2.centerXAnchor).isActive = true
stackView2.topAnchor.constraint(equalTo: stackView1.bottomAnchor, constant: 40).isActive = true
stackView1.addArrangedSubview(makeButton("Button", backgroundColor: .red))
stackView1.addArrangedSubview(makeButton("Button Very Wild!!", backgroundColor: .blue))
stackView1.addArrangedSubview(makeButton("Button mid size", backgroundColor: .yellow))
stackView1.addArrangedSubview(makeButton("But", backgroundColor: .cyan))
stackView2.addArrangedSubview(makeButtonWithConfiguration("Button", backgroundColor: .red))
stackView2.addArrangedSubview(makeButtonWithConfiguration("Button Very Wild!!", backgroundColor: .blue))
stackView2.addArrangedSubview(makeButtonWithConfiguration("Button mid size", backgroundColor: .yellow))
stackView2.addArrangedSubview(makeButtonWithConfiguration("But", backgroundColor: .cyan))
}
func makeButton(_ title: String, backgroundColor: UIColor) -> UIButton {
let button = UIButton()
button.setTitle(title, for: .normal)
button.backgroundColor = backgroundColor
return button
}
func makeButtonWithConfiguration(_ title: String, backgroundColor: UIColor) -> UIButton {
var configuration = UIButton.Configuration.filled()
configuration.title = title
configuration.baseBackgroundColor = backgroundColor
let button = UIButton(configuration: configuration)
return button
}
}
The default UIButton.Configuration.titleLineBreakMode
is .byWordWrapping
, which means the title can be multi-line. Compare this to the default lineBreakMode
of a UIButton
without Configuration
- .byTruncatingTail
, which means the title can only be one line.
You can set it to .byClipping
, so that the title is only one line. As a result, there is a "natural size" for the button.
var configuration = UIButton.Configuration.plain()
configuration.titleLineBreakMode = .byTruncatingTail
// you can also remove these insets and set the font size
// if you want it to look exactly like the buttons without a configuration
configuration.contentInsets.leading = 0
configuration.contentInsets.trailing = 0
configuration.titleTextAttributesTransformer = .init {
$0.merging(.init().font(UIFont.systemFont(ofSize: 18)))
}