macosswiftuigrid

Laying out controls in a grid


This is a follow up to my previous question: Aligning left side of TextFields

I am trying to use a Grid to mimic the layout of controls as seen in Xcode:

enter image description here

I am almost there, this is my current result (just ignore the values in the Picker):

enter image description here

How can I fix my code so that the TextField and Stepper in the HStack look as in the Xcode example?

macOS 15.6.1, Xcode 16.4

Here is my code:

import SwiftUI

struct ContentView: View {
    enum Setting: String, CaseIterable {
        case one
        case twp
        case three
    }

    @State private var unit: Setting = .one
    @State private var quantity1: Int = 4
    @State private var quantity2: Int = 4

    static let numberFormatter = NumberFormatter()

    var body: some View {
        ScrollView {
            Grid(horizontalSpacing: 5, verticalSpacing: 5) {
                GridRow {
                    Text("Text Encoding")
                        .font(.system(size: 11))
                        .gridColumnAlignment(.trailing)
                    Picker("", selection: $unit) {
                        ForEach(Setting.allCases, id: \.self) { unit in
                            Text(unit.rawValue)
                        }
                    }
                    .gridCellColumns(2)
                    .pickerStyle(.menu)
                    .controlSize(.small)
                }

                GridRow {
                    Text("Line Endings")
                        .font(.system(size: 11))
                        .gridColumnAlignment(.trailing)
                    Picker("", selection: $unit) {
                        ForEach(Setting.allCases, id: \.self) { unit in
                            Text(unit.rawValue)
                        }
                    }
                    .gridCellColumns(2)
                    .pickerStyle(.menu)
                    .controlSize(.small)
                }

                Divider()

                GridRow {
                    Text("Indent Using")
                        .font(.system(size: 11))
                        .gridColumnAlignment(.trailing)
                    Picker("", selection: $unit) {
                        ForEach(Setting.allCases, id: \.self) { unit in
                            Text(unit.rawValue)
                        }
                    }
                    .gridCellColumns(2)
                    .pickerStyle(.menu)
                    .controlSize(.small)
                }

                GridRow {
                    Text("Widths")
                        .font(.system(size: 11))
                        .gridColumnAlignment(.trailing)
                    
                    HStack {
                        TextField("", value: $quantity1, formatter: numberFormatter)
                        Stepper("", value: $quantity1)
                    }
                    .controlSize(.small)
                    .font(.system(size: 11))

                    HStack {
                        TextField("", value: $quantity2, formatter: numberFormatter)
                        Stepper("", value: $quantity2)
                    }
                    .controlSize(.small)
                    .font(.system(size: 11))
                }

                Divider()
            }
            .padding()
        }
    }
}

Solution

  • The reason why there is more gap between the labels and the controls in the first three rows is because the empty labels are occupying some space. This can be avoided by applying .labelsHidden() to each Picker. But since the Stepper have no labels either, you can just apply it to the whole Grid instead:

    Grid(horizontalSpacing: 5, verticalSpacing: 5) {
        // ...
    }
    .labelsHidden() // 👈 here
    .padding()
    

    Screenshot