Search code examples
swiftswiftuiscrollview

View is not updating on Category Menu Item Selection SwiftUI


enter image description hereThis is my first SwiftUI app. I have done a lot of searches and could not find a solution to this.

I want to display Products from different categories. For that, I have top bar horizontally scrollable menu. When selecting any category, I want to display each category's products. When on loading first category products are displaying correctly, but selecting any other category, products are not updating accordingly. However, When I navigate to any other view and come back to this view, products are displaying correctly.

What I tried

I tried different views to display products like List/ScrollView. No luck.

Here is the complete code of my View

struct ProductListView: View {

    @State var data:[Product]
    @State private var categoryItems:[Product] = []
    @State private var uniqueCategories:[Product] = []
    @State private var numberOfRows:Int  = 0
    @State private var selectedItem:Int  = 0
    @State private var image:UIImage?

    @EnvironmentObject var authState: AuthenticationState

    @ViewBuilder
    var body: some View {

        NavigationView{
            VStack{
                ScrollView(.horizontal, showsIndicators: false) {
                    HStack {
                        ForEach(0..<self.data.removingDuplicates(byKey: { $0.category }).count) { i in
                            Text(self.data.removingDuplicates(byKey: { $0.category })[i].category)
                                .underline(self.selectedItem == i ? true:false, color: Color.orange)
                                .foregroundColor(self.selectedItem == i ? Color.red:Color.black)
                                //.border(Color.gray, width: 2.0)
                                .overlay(
                                    RoundedRectangle(cornerRadius: 0.0)
                                        .stroke(Color.init(red: 236.0/255.0, green: 240.0/255.0, blue: 241.0/255.0), lineWidth: 1).shadow(radius: 5.0)
                            ).padding(.horizontal)
                                //.shadow(radius: 5.0)

                                .onTapGesture {

                                    self.selectedItem = i
                                    _ = self.getSelectedCategoryProducts()
                                    print("Category Items New Count: \(self.categoryItems.count)")


                            }
                            Spacer()
                            Divider().background(Color.orange)
                        }
                    }
                }
                .frame(height: 20)

                Text("My Products").foregroundColor(Color.red).padding()

                Spacer()

                if(self.data.count == 0){
                    Text("You didn't add any product yet.")
                }

                if (self.categoryItems.count>0){

                    ScrollView(.vertical){

                        ForEach(0..<Int(ceil(Double(self.categoryItems.count)/2.0)), id: \.self){ itemIndex in
                            return HStack() {

                                NavigationLink(destination:ProductDetailView(index: self.computeIndexes(currentIndex: itemIndex,cellNo: 1), data: self.categoryItems, image: UIImage())){


                                    ProductTile(index: self.computeIndexes(currentIndex: itemIndex, cellNo: 1), data: self.categoryItems, image: UIImage())


                                }

                                NavigationLink(destination:ProductDetailView(index: self.computeIndexes(currentIndex: itemIndex,cellNo: 2), data: self.categoryItems, image: UIImage())){
                                    ProductTile(index: self.computeIndexes(currentIndex: itemIndex, cellNo: 2), data: self.categoryItems, image: UIImage())
                                }

                            }.padding(.horizontal)
                        }


                    }.overlay(
                        RoundedRectangle(cornerRadius: 10.0)
                            .stroke(Color.init(red: 236.0/255.0, green: 240.0/255.0, blue: 241.0/255.0), lineWidth: 1).shadow(radius: 5.0)
                    )
                }
            }

        }

        .onAppear(perform: {

            _ = self.getSelectedCategoryProducts()

            //print("Loading updated products....")
            if (self.authState.loggedInUser != nil){
                FireStoreManager().loadProducts(userId: self.authState.loggedInUser!.uid) { (isSuccess, data) in

                    self.data = data

                }
            }
        })

            .padding()


    }

    func populateUniqueCategories() -> [Product] {

        let uniqueRecords = self.data.reduce([], {
            $0.contains($1) ? $0 : $0 + [$1]
        })

        print("Unique Items: \(uniqueRecords)")

        return uniqueRecords
    }

    func getSelectedCategoryProducts() -> [Product] {

        var categoryProducts:[Product] = []

        self.data.forEach { (myProduct) in
            if(myProduct.category == self.populateUniqueCategories()[selectedItem].category){
                categoryProducts.append(myProduct)
            }
        }
        self.categoryItems.removeAll()
        self.categoryItems = categoryProducts
        return categoryProducts
    }
    func computeIndexes(currentIndex:Int, cellNo:Int) -> Int {
        var resultedIndex:Int = currentIndex

        if (cellNo == 1){
            resultedIndex = resultedIndex+currentIndex
            print("Cell 1 Index: \(resultedIndex)")
        }else{
            resultedIndex = resultedIndex + currentIndex + 1
            print("Cell 2 Index: \(resultedIndex)")
        }
        return resultedIndex
    }
}

And this is the array extension

    extension Array {
    func removingDuplicates<T: Hashable>(byKey key: (Element) -> T)  -> [Element] {
        var result = [Element]()
        var seen = Set<T>()
        for value in self {
            if seen.insert(key(value)).inserted {
                result.append(value)
            }
        }

        return result
    }
}

I would really thankful if you help me to sort out this issue. Best Regards


Solution

  • The reason is in using static range ForEach constructor, which does not expect any changes, by design, so not updated.

    So instead of using like

    ForEach(0..<self.data.removingDuplicates(byKey: { $0.category }).count) { i in
           ^^^^^^ range !!
    
    

    and

    ForEach(0..<Int(ceil(Double(self.categoryItems.count)/2.0)), id: \.self){
            ^^^^ range
    

    it needs to use direct data container, like

    ForEach(self.data.removingDuplicates(byKey: { $0.category }), id: \.your_product_id) { product in
            ^^^^^^^^^  array
    

    Note: if you need index during iteration, it is possible by using .enumerated() to container, eg: in this post