applying fixedSize only if there is enough space

Viewed 40

I want to build a VStack that is only as wide as the widest element is. If there is (horizontally) not enough space, e.g., the screen is too narrow, the content should shrink. Here is a simple example:

    VStack {
            HStack {
                Text("title1")
                Spacer()
                Text("title2")
                Spacer()
                Text("title3")
            }
            HStack {
                Text("fo0o0o0o0o0o0o0o0oo0o0o0")
                Spacer()
                Text("fo0o0o0o0o")
                Spacer()
                Text("fo0")
            }
            HStack {
                Text("fo0o0o0o0o")
                Spacer()
                Text("fo0o")
                Spacer()
                Text("fo0")
            }
    }.fixedSize()

How it should look like, if the VStack does not have to shrink.

How it should look like, if the VStack does not have to shrink.


How it should look like, if the VStack has to shrink.

How it should look like, if the VStack has to shrink.


If fixedSize is applied, the content obviously does not shrink and goes over the edges of the screen. If fixedSize is not applied, the spacers use all (horizontally) available space and the table is too wide. Hard coding the width is not an option because the content is dynamic and, therefore, unknown.

Basically: if there is enough space, apply fixedSize. If not, don't apply fixedSize and leave it as it is.

How can I archive that behavior with SwiftUI?

2 Answers

I hope I understood what you mean. If you are using Xcode 14 and can deploy iOS 16 or later, you can simply replace VStack with Grid and HStack with GridRow; this will place the text in columns. Then, you can remove the Spacer() all over. If the text is too long, it will increase the lines inside its own column.

Like this:

        Grid {
            GridRow {
                Text("title1")
                Text("title2")
                Text("title3")
            }
            GridRow {
                Text("fo0o0o0o0o0o0o0o0")
                Text("fo0o0o0o0o")
                Text("fo0")
            }
            GridRow {
                Text("fo0o0o0o0o")
                Text("fo0o")
                Text("fo0")
            }
        }

Result in the images (short vs. long text): enter image description here

enter image description here

you could try this approach, using .fixedSize(horizontal: _, vertical: _) and some space calculation logic, together with a @State var.

struct ContentView: View {
    @State var dofix = false  // <-- here
    
    var body: some View {
        VStack {
            // for testing, do your space calculation logic
            Button("dofix") {
                dofix.toggle() // <-- here
            }
            HStack {
                Text("title1")
                Spacer()
                Text("title2")
                Spacer()
                Text("title3")
            }
            HStack {
                Text("fo0o0o0o0o0o0o0o0oo0o0o0")
                Spacer()
                Text("fo0o0o0o0o")
                Spacer()
                Text("fo0")
            }
            HStack {
                Text("fo0o0o0o0o")
                Spacer()
                Text("fo0o")
                Spacer()
                Text("fo0")
            }
        }
        .fixedSize(horizontal: dofix, vertical: dofix) // <-- here
    }
    
}
Related