简体   繁体   中英

My view doesn't update after modifying an object of a list inside an ObservableObject

I am new to SwiftUI. Imagine I am building an app for a shop. I have a view that is a list of the details of a product, and two buttons: Pin (add to favorites) and Delete .

My question is about the Pin button (the Delete button works fine). When I click on it, the icon is supposed to change to a filled pin icon instead of the classic pin icon. And when I go back the previous view, listing the 5 latest products and the pinned products (in two different sections), this one is supposed to update and add the modified product to the pinned product list.

But right now, the pin icon doesn't change at all (but does when I go back and come again to the product details view) and the previous view doesn't show the product in the pinned product list (and I can't check that it appears when I reload the view, because I can't go back and come again to this view as this is the main view).

I used some tags to indicate that the view does modify the store and the products themselves as you can see below.

So the view has an attribute for the product itself and one for the store — the repository that controls the list of all products:

struct DetailsView: View {
    @StateObject var store: Store
    @State var product: Product
    
    var body: some View {
        List {
            Section {
                VStack(alignment: .leading) {
                    Text(product.name)
                    
                    Text(product.brand)
                        .font(.subheadline)
                        .foregroundColor(.secondary)
                }
                .padding(.vertical, 6.0)
            } header: {
                Text("Product")
            }
            
            Section {
                Button {
                    product.pinned = !product.pinned
                } label: {
                    HStack {
                        if (product.pinned) {
                            Image(systemName: "pin.fill")
                            Text("Unpin")
                        } else {
                            Image(systemName: "pin")
                            Text("Pin")
                        }
                    }
                    .padding(.vertical, 6.0)
                }
                
                Button {
                    store.remove(product.id)
                } label: {
                    HStack {
                        Image(systemName: "trash")
                        Text("Delete")
                    }
                    .foregroundColor(.red)
                    .padding(.vertical, 6.0)
                }
            }
        }
        .navigationTitle(product.name)
        .navigationBarTitleDisplayMode(.inline)
    }
}

My store is only defined by one attribute, the list of products:

class Store: ObservableObject {
    @Published private var products: [Product]
    
    …
}

Finally, a product is defined by different attributes (name, brand, pinned):

class Product: Identifiable {
    static private var count: Int = 0
    
    let id: Int
    @Published var name: String
    @Published var brand: String
    @Published var pinned: Bool = false
    
    …
}

To recap, my problem is: my current view is not updated (the pin button doesn't change) and my previous view is not updated when I go back to it (the product doesn't appear in the pinned products list).

Does anyone have an idea on how to fix this? Thank you very much.

PS
My previous view showing the pinned items is working fine except this. I tested it by initializing my store with pinned items and they appear.

You have 2 questions:

1, the pin status of your current page does not change when you click the pin icon.

Why? Because the Product class does not inherit ObservableObject class, so it cannot be observed. To fix it, inherit ObservableObject class.

class Product: ObservableObject, Identifiable {
   static private var count: Int = 0

   let id: Int
   @Published var name: String
   @Published var brand: String
   @Published var pinned: Bool = false

   …
}

And in your DetailsView, remember to observe product:

struct DetailsView: View{
   @ObeservedObject product: Product
   ...
}

2, your previous page does not change when you click pin in current page.

The reason is that although the Store class is an observabledObject, which means the published property "products" is observed by others, the object of the array "products" is "product", which is a class.

So actually the array "products" holds some pointers to "product". Even when the product's properties like "pined" changes, the pointer(address) does not change. So when someone observe the published "products", he will say "Oh, nothing happened. No need to refresh the view."

To fix it, in your previous view, you should observe the product, rather than the store.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM