Search code examples
swiftuiswiftui-list

Swiftui List @State could not select Struc View


This particular SwiftUI List, made up of array, I can not seem to select it. Even though I tried with Set(). or @Binding. everything online resource has to offer. Wrong type? or something might have to be about Identifiable? View? Array? I'm totally new here. Related document would be appreciated.

And also how can I access/take the data of selected item and append it into another Array let's say a cart (for e-commerce app for example)

Thank you in advance

import SwiftUI

struct CategoryT1: View {
    //
    @State private var a1: String?
    @State private var a2: SingleItem?
    // @Binding private var a3: SingleItem?   // Doesn't work & cause other error
    // @State private var a3 = Set<SingleItem>() // No Error, but still can't select
    @State private var a3: SingleItem? // This couldn't get select
    //***** Array 1
    let users = ["John", "Jane", "Jim", "Jill"]
    //***** Array 2
    var arrayOfSingleItem = [
        SingleItem(name: "car",
                   idd: "NYC-0202",
                   size: "SUV",
                   price3: "5",
                   price: "6"),
        SingleItem(name: "tank",
                   idd: "NYC-2",
                   size: "Big",
                   price3: "7",
                   price: "9")
    ]
    //***** Array 3
    var dataOfItem =
    [
        ["Candy","0123456","small","95","95"  ],
        ["Cake","005500","10 pound","108","108" ],
    ]
    //*****
    var body: some View {
        Text("Category")
        //**** 1
        List(users, id:\.self, selection: $a1) { user in
            Text(user)
        }
        //**** 2
        List(arrayOfSingleItem, id:\.self, selection: $a2) { x in
            CellRowView(product: x)
        }
        //**** 3   // This couldn't get select
        List(dataOfItem, id:\.self, selection: $a3) { x in
            CellRowView(product: SingleItem(
                name: x[0],
                idd: x[1],
                size: x[2],
                price3: x[3],
                price: x[4])
            )
        }
        
    }
}

#Preview {
    CategoryT1()
}

SingleItem.swiftui would look like this

import SwiftUI

struct SingleItem: Identifiable, Hashable {
     let id = UUID()
     var name: String
     var idd:  String
     var size: String
     var price3: String
     var price: String
 }
//
struct CellRowView: View {
    var product: SingleItem

    var body: some View {
        VStack(alignment: .leading, spacing: 3) {
            Text(product.name)
                .foregroundColor(.primary)
                .font(.headline)
            HStack(spacing: 3) {
                Text(product.idd)
            }
            .foregroundColor(.secondary)
            .font(.subheadline)
        }
    }
}

Solution

  • Try this approach where you use a .tag(a2) to identify your a2 selection, same for a3.

    Note, your dataOfItem is an array of array of Strings, re-make it into a array of strutc to make it easier to work with.

    Note, you should also not use x[..] in your real code. Similarly, do not use id:\.self in the lists, ensure the items are unique Identifiable.

    To ... access/take the data of selected item and append it into another Array just use the selections variables a1, a2 or a3, and to trigger some function when they change, use .onChange(of: ...)

    For more info, read the basics of SwiftUI again, especially @State and List.

    struct ContentView: View {
        var body: some View {
            CategoryT1()
        }
    }
    
    struct CategoryT1: View {
        @State private var a1: String?
        @State private var a2: SingleItem?
        @State private var a3: SingleItem?
        
        //***** Array 1
        let users = ["John", "Jane", "Jim", "Jill"]
        
        //***** Array 2
        var arrayOfSingleItem = [
            SingleItem(name: "car",
                       idd: "NYC-0202",
                       size: "SUV",
                       price3: "5",
                       price: "6"),
            SingleItem(name: "tank",
                       idd: "NYC-2",
                       size: "Big",
                       price3: "7",
                       price: "9")
        ]
        
        //***** Array 3
        var dataOfItem =
        [
            ["Candy","0123456","small","95","95"  ],
            ["Cake","005500","10 pound","108","108" ]
        ]
    
        var body: some View {
            Text("Category")
            //**** 1
            List(users, id:\.self, selection: $a1) { user in
                Text(user)
            }
            //**** 2
            List(arrayOfSingleItem, selection: $a2) { x in
                CellRowView(product: x)
                    .tag(x) // <--- here
            }
            //**** 3
            List(dataOfItemAsArrayOfSingleItem(), selection: $a3) { item in
                CellRowView(product: item)
                    .tag(item) // <--- here
            }
            
            // for testing to display the selections
            Text("selected a1 user: \(a1 ?? "no user")")
            Text("selected a2 SingleItem: \(a2?.name ?? "nothing")")
            Text("selected a3 SingleItem: \(a3?.name ?? "nothing")")
    
                .onChange(of: a1) {
                    print("---> a1 value changed: \(a1 ?? "no user")")
                }
        }
        
        // --- here
        func dataOfItemAsArrayOfSingleItem() -> [SingleItem] {
             dataOfItem.map {
                SingleItem(name: $0[0], idd: $0[1], size: $0[2], price3: $0[3], price: $0[4])
            }
        }
    }
    

    Ok, for the row highlight, try this alternative example of the code, where dataOfItemAsArrayOfSingleItem is an array of [SingleItem], not a function, as in the previous example code.

    struct CategoryT1: View {
        @State private var a1: String?
        @State private var a2: SingleItem?
        @State private var a3: SingleItem?
        
        @State private var dataOfItemAsArrayOfSingleItem: [SingleItem] = [] // <--- here
        
        //***** Array 1
        let users = ["John", "Jane", "Jim", "Jill"]
        
        //***** Array 2
        var arrayOfSingleItem = [
            SingleItem(name: "car", idd: "NYC-0202", size: "SUV", price3: "5", price: "6"),
            SingleItem(name: "tank", idd: "NYC-2", size: "Big", price3: "7", price: "9")
        ]
        
        //***** Array 3
        var dataOfItem =
        [
            ["Candy","0123456","small","95","95"  ],
            ["Cake","005500","10 pound","108","108" ]
        ]
    
        var body: some View {
            Text("Category")
            //**** 1
            List(users, id:\.self, selection: $a1) { user in
                Text(user)
            }
            //**** 2
            List(arrayOfSingleItem, selection: $a2) { x in
                CellRowView(product: x)
                    .tag(x) // <--- here
            }
            //**** 3
            List(dataOfItemAsArrayOfSingleItem, selection: $a3) { item in
                CellRowView(product: item)
                    .tag(item) // <--- here
            }
            
            // for testing to display the selections
            Text("selected a1 user: \(a1 ?? "no user")")
            Text("selected a2 SingleItem: \(a2?.name ?? "nothing")")
            Text("selected a3 SingleItem: \(a3?.name ?? "nothing")")
    
                .onChange(of: a1) {
                    print("---> a1 value changed: \(a1 ?? "no user")")
                }
            
                // <--- here, populate the array on start
                .onAppear {
                    dataOfItemAsArrayOfSingleItem = dataOfItem.map {
                        SingleItem(name: $0[0], idd: $0[1], size: $0[2], price3: $0[3], price: $0[4])
                    }
                }
        }
    
    }