Home > OS >  SwiftUI picker .onChanged only firing on 2 selection changes
SwiftUI picker .onChanged only firing on 2 selection changes

Time:04-04

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
}

CodePudding user response:

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.

CodePudding user response:

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)
    }
}
  • Related