Home > database >  Multiple controls on same line in SwiftUI macOS form
Multiple controls on same line in SwiftUI macOS form

Time:06-23

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 )

CodePudding user response:

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 {
        Form {
            LazyVGrid(columns: columns) {
                Text("Name:")
                TextField("", text: $data.name)
                    .frame(width: 200.0)
                Text("Visible:")
                HStack {
                    Text("").frame(width: 3.0)
                    Toggle("", 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)
                }
            }
        }
        .padding(.all)
    }
    
    var body : some View {
        VStack {
            form
            Text(" --- Check --- ")
            Text(String(describing: data))
        }
    }
    
}

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

CodePudding user response:

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()
    }
}
  • Related