skip to Main Content

I have a picker that will change an attribute of an object that is bound to the view. When the picker is changed I want to update the bound object so the change is apparent in the previous view. The picker updates the label on each change but the .onChanged modifier does’t fire after the first change. If changed 2 or more times the .onChanged body fires.

let categories: [String] = ["None", "Produce", "Dairy/Eggs", "Meat", "Breads", "Canned Goods", "Baking", "Frozen", "Bulk", "Snack Foods", "Spices/Seasonings", "Pasta/Rice", "Drinks", "Liquor", "Condiments"]

    //Name of recipe received from previous view
    @Binding var ingredient: Ingredient
   
    
    //ingredient variable that can be updated
    @State var category: String = ""

    var body: some View {
        VStack(alignment: .leading) {
                Text(ingredient.name)
                    .font(.title)
                    .padding(.leading, 5)
                Menu {
                    Picker("picker", selection: $category) {
                        ForEach(categories, id: .self) {
                            Text($0)
                        }
                    }
                    .onChange(of: category, perform: { newValue in
                        print("PICKER CHANGED")
                        ingredient.category = self.category

                    })
                    .labelsHidden()
                    .pickerStyle(InlinePickerStyle())
                    
                } label: {
                    Text(category)
                        .foregroundColor(.black)
                        .padding(5)
                        .labelsHidden()
                        .clipped()
                        .mask(RoundedRectangle(cornerRadius: 20, style: .continuous))
                }
         }
    .onAppear(perform: {self.category = ingredient.category})
}

Ingredient class:

class Ingredient: Identifiable, Hashable{
    static func == (lhs: Ingredient, rhs: Ingredient) -> Bool {
        if (lhs.id == rhs.id) {return true}
        else {return false}
    }
    
    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
    }
    
    public var id = UUID()
    public var name: String = ""
    public var inStock: Bool = false
    public var category: String = ""
    public var keepInStock: Bool = false
}

2

Answers


  1. Have you tried setting a tag modifier on the Text property inside the ForEach for the picker?

    Picker("picker", selection: $category) {
        ForEach(categories, id: .self) {
            Text($0).tag($0)
        }
    }
    
    Login or Signup to reply.
  2. This issue is because SwiftUI does not know about the changes.

    Ingredient is a class change it to a struct

    OR

    make it an ObservableObject wrap the variables in @Published and change

    @Binding var ingredient: Ingredient
    

    to

    @ObservedObject var ingredient: Ingredient
    

    class is reference type and struct is value type.

    @State, @Binding, @Published see changes for value type.

    @ObservedObject, @StateObject, and @EnvironmentObject are for reference. Accompanied by ObservableObject and @Published

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search