ice-wind
ice-wind

Reputation: 868

Multiple controls on same line in SwiftUI macOS form

I am making a UI to change the 3D coordinates of an object, and I thought it would make sense to put all three on the same line with a label beforehand, sort of like System Preferences does for number separators : Number Separators in Systemm Preferences

However, doing so messes up the alignment of the whole form, and I'm not sure how to resolve this (except by adding VStacks and HStacks everywhere, which I really hope is not the best available solution) : My messed up UI

Here is the code driving the view :

struct ObjectSettingsView: View {
    @State var object : Object
    
    var body : some View {
        Form {
            TextField("Name:", text: $object.name, prompt : Text("New Object"))
            Toggle(
                "Visible",
                isOn: $object.visible
            )
            Divider()
            HStack {
                Text("Coordinates:")
                NumberView(label : "X:", number : object.coordinates.x)
                NumberView(label : "Y:", number : object.coordinates.y)
                NumberView(label : "Z:", number : object.coordinates.z)
            }
        }
    }
}

struct NumberView : View{
    var label : String
    @State var number : Int32
    
    var body : some View {
        HStack {
            TextField(
                self.label,
                value: self.$number,
                formatter: NumberFormatter()
            )
            Stepper("", value: self.$number, in: 1...8)
                .labelsHidden()
        }
    }
}

( I know this really should be using a ViewModel, I'm just trying to figure out how forms work right now )

Upvotes: 2

Views: 642

Answers (3)

dtm
dtm

Reputation: 51

// static let left = GridItem.Size.flexible(minimum: 10, maximum: .infinity)
static let left = GridItem.Size.flexible(minimum: 40, maximum: 100)
static let right = GridItem.Size.flexible(minimum: 40, maximum: 200)
let columns = [
    GridItem(left, alignment: .trailing),
    GridItem(right, alignment: .leading),
]

The problem is that the size structs are set to .infinity I changed the column settings like above with fixed max values. To get all infos see Apple Docs about GridItem.

Upvotes: 0

dtm
dtm

Reputation: 51

I add @Binding and LazyVGrid to your Code.

Maybe this helps:

struct ObjectData {
    var name: String = "New Object"
    var visible: Bool = true
    var coordinates_x: Int32 = 0
    var coordinates_y: Int32 = 0
    var coordinates_z: Int32 = 0
}

struct ContentView: View {
    @State var data = ObjectData()

    let columns = [
        GridItem(alignment: .trailing),
        GridItem(alignment: .leading),
    ]

    var form: some View {
        LazyVGrid(columns: columns) {
            Text("Name:")
            TextField("", text: $data.name)
            Text("blind").opacity(0)
            Toggle("Visible:", isOn: $data.visible)
            Text("Coordinates:")
            HStack {
                NumberView(label : "X:", number : $data.coordinates_x)
                NumberView(label : "Y:", number : $data.coordinates_y)
                NumberView(label : "Z:", number : $data.coordinates_z)
            }
        }
    }
    
    var body : some View {
        VStack() {
            form
            Text(" --- Check --- ")
            Text(String(describing: data))
        }
        .frame(width: 400.0)
    }
    
}

struct NumberView : View{
    var label : String
    @Binding var number : Int32
    
    var body : some View {
        HStack {
            TextField(
                self.label,
                value: self.$number,
                formatter: NumberFormatter()
            )
            Stepper("", value: self.$number, in: 1...8)
                .labelsHidden()
        }
    }
}

Upvotes: 1

ice-wind
ice-wind

Reputation: 868

Separating things into two Forms almost does the trick, although labels are still not exactly aligned as in system Preferences :

enter image description here

struct ObjectSettingsView: View {
    @State var object : Object
    
    var body : some View {
        VStack {
            Form {
                TextField("Name:", text: $object.name, prompt : Text("New Object"))
                Toggle("Visible", isOn: $object.visible)
            }
            Divider()
            Form {
                HStack {
                    Text("Coordinates:")
                    NumberView(label : "X:", number : object.coordinates.x)
                    NumberView(label : "Y:", number : object.coordinates.y)
                    NumberView(label : "Z:", number : object.coordinates.z)
                }
            }
        }
        .padding()
    }
}

Upvotes: 0

Related Questions