0

I use Xcode 14.2, macOS Monterey 12.6.2 and Minimum Deployments 15.5. I want to display 4 TextFields in a 2x2 grid for integer input. The data is stored in an extern struct Model as an ObservableObject. The environment variable is injected in the @main struct.

The TextField must be equipped with an .id modifier. Otherwise the following error message appears:

LazyVGridLayout: the ID 0 is used by multiple child views, this will give undefined results! LazyVGridLayout: the ID 1 is used by multiple child views, this will give undefined results!

Adding .id(row + col) results in a single error message:

LazyVGridLayout: the ID 1 is used by multiple child views, this will give undefined results!

Using UUID() for generating an ID .id(UUID()) results in a strange effect. Trying to enter a multi digit value in a Textfeld fails. The software keyboard vanishes after the input of the first digit.

Changing the id to .id(row + 7 * col) results in the expected behavior of the demo App. However, this "solution" shouldn't be the right way to solve the problem.

Has somebody an idea was is going wrong?

struct ContentView: View {
    let colums = [GridItem(),GridItem()]
    @EnvironmentObject var model: Model

    
    var body: some View {
        List {
            LazyVGrid(columns: colums) {
                ForEach(0...1, id:\.self) { row in
                    ForEach(0...1, id: \.self) { col in
                        TextField("", value: $model.rows[row].values[col], format: .number)
                            .id(UUID())
//                            .id(row + 7 * col)

                    }
                }
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}


class Model: ObservableObject {
    @Published
    var rows = [Values(), Values()]
    
    struct Values {
        var values = [0, 0]
    }
}


@main
struct KeyboardFocusApp: App {
    @StateObject var model = Model()
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(model)
        }
    }
}
Heinz M.
  • 668
  • 3
  • 8
  • it should be `.id(row * 2 + col)` ... and better would be if your data structure has its own id. – ChrisR Jan 15 '23 at 20:42

1 Answers1

0

Following my comment please let me suggest a different approach. The whole fun of VGrid is that you don't need the grid structure in your data. You define the column and just throw the data in.

Also as commented it would be preferable to have the model data itself identifiable.

struct ContentView: View {
    
//    @EnvironmentObject var model: Model
    @StateObject var model = Model()

    let colums = [GridItem(),GridItem()]
    
    var body: some View {
        List {
            LazyVGrid(columns: colums) {
                // no need for .id, as items are identifiable
                // $ init makes $item modifiable
                ForEach($model.items) { $item in
                    TextField("", value: $item.value, format: .number)
                }
            }
        }
    }
}


class Model: ObservableObject {
    
    init() { // initialize with 4 items
        items = []
        for _ in 0..<4 {
            items.append(Item())
        }
    }
    
    @Published var items: [Item]
    
    struct Item: Identifiable {
        let id = UUID()
        var value = 0
    }
}
ChrisR
  • 9,523
  • 1
  • 8
  • 26