SwiftUI: buttons inside a HStack cause the overall HStack to exceed the bounds
I have a SwiftUI which holds a number of buttons inside of an HStack
. These buttons have both an icon and some text, laid out vertically. I am running into the problem where the buttons can become too wide: the HStack is going outside of the bounds of the view itself. It would be logical if the "Download all" button would lay its text on two lines for example, but it's not doing that.
Preview example:
As you can see, the first version has the problem, the three buttons don't fit anymore. But even in the second example the rounded corners don't completely show - only the third example is 100% showing correctly.
Code:
import SwiftUI
struct TransferDetailsButtonsView: View {
enum ButtonType: Hashable {
case share
case download
case delete
fileprivate var imageName: String {
switch self {
case .share:
return "icon-share"
case .download:
return "icon-download"
case .delete:
return "icon-delete"
}
}
fileprivate var title: String {
switch self {
case .share:
return "Share"
case .download:
return "Download all"
case .delete:
return "Delete"
}
}
}
/// The button types you want to show
var buttonTypes: [ButtonType] = [.share, .download, .delete]
/// The action for the buttons
var action: (ButtonType) -> Void = { _ in }
var body: some View {
HStack(spacing: 0) {
Spacer(minLength: 20)
.frame(maxWidth: .infinity)
ForEach(buttonTypes, id: \.self) { button in
Button {
action(button)
} label: {
VStack(spacing: 8) {
Image(button.imageName)
Text(button.title)
.lineLimit(nil)
}
.fixedSize()
}
Spacer(minLength: 20)
.frame(maxWidth: .infinity)
}
}
.padding(.vertical, 12)
.foregroundColor(.white)
.background(RoundedRectangle(cornerRadius: 16).fill(.blue))
}
}
struct TransferDetailsButtonsView_Previews: PreviewProvider {
static var previews: some View {
Group {
TransferDetailsButtonsView()
.frame(width: 260)
.previewLayout(.sizeThatFits)
TransferDetailsButtonsView()
.frame(width: 300)
.previewLayout(.sizeThatFits)
TransferDetailsButtonsView()
.frame(width: 420)
.previewLayout(.sizeThatFits)
}
}
}
How can I make it so that the HStack
doesn't go outside of the overall bounds, but instead will use multiline text for the button texts?
Solution 1:
Your fixedSize()
was making it draw the HStack
outside of it's bounds. You want the Texts to fill the available space, then SwiftUI will try to break on the words. If the container is too small it will break within the words so you need to be aware of this, 260 is about the smallest it can go with this font size.
Here's what I came up with, modified to be runnable with an SF symbol. You need some padding in between texts otherwise they will be right up against each other at some sizes of the container.
struct TransferDetailsButtonsView: View {
enum ButtonType: Hashable {
case share
case download
case delete
fileprivate var imageName: String {
switch self {
case .share:
return "square.and.arrow.up.fill"
case .download:
return "square.and.arrow.up.fill"
case .delete:
return "square.and.arrow.up.fill"
}
}
fileprivate var title: String {
switch self {
case .share:
return "Share"
case .download:
return "Download all"
case .delete:
return "Delete it now"
}
}
}
/// The button types you want to show
var buttonTypes: [ButtonType] = [.share, .download, .delete]
/// The action for the buttons
var action: (ButtonType) -> Void = { _ in }
var body: some View {
HStack(alignment: .top, spacing: 0) {
ForEach(buttonTypes, id: \.self) { button in
Button {
action(button)
} label: {
VStack(spacing: 8) {
Image(systemName: button.imageName)
Text(button.title)
.frame(maxWidth: .infinity)
}
}
.padding(.horizontal, 4)
}
}
.padding(.vertical, 12)
.foregroundColor(.white)
.background(RoundedRectangle(cornerRadius: 16).fill(.blue))
}
}