2
votes

So I have a ParentView which contains a FilterBar and a List. It looks something like this:

struct ParentView: View {
    @State var listCellModels: [ListCellModel]

    // Both these vars are passed to the FilterBar and adjusted by the FilterBar
    @State var isEditing: Bool = false
    @State var selectedType: FilterType = .none

    // When selected type is changed, I need to reload the models in the list with the new filter
    private var filteredModels: [ListCellModel] {
        return listCellModels.filter{
            (selectedType.rawValue == 0 || $0.approved.rawValue == selectedType.rawValue)
        }
    }

    var body: some View {
        VStack {
            FilterBar(isEditing: $isEditing, selectedType: $selectedType)

            // All the items in the list are buttons that display a custom view I had
            // this works fine, and when isEditing is changed the view DOES update
            List(filteredModels) { model in
                Button(action: {
                    // Does a thing
                }, label: {
                    ListViewCell(model: model, isEditing: self.$isEditing)
                })
            }
        }
    }
}

My Filter bar is just a simple HStack with a couple buttons that modify the variables

struct FilterBar: View {
    @Binding var isEditing: Bool
    @Binding var selectedType: FilterType

    var body: some View {
        HStack(alignment: .center) {
            Button(action: {
                self.selectedType = FilterType.init(rawValue: (self.selectedType.rawValue + 1) % 4)!
            }, label: {
                Text("Filter: \(selectedType.name)")
            }).padding(.top).padding(.leading)

            Spacer()

            Button(action: {
                self.isEditing = !self.isEditing
            }, label: {
                Text(!isEditing ? "Edit" : "Done")
            }).padding(.top).padding(.trailing)
        }
    }
}

When I tap the button that changes isEditing, all of the cells in the list update to show their "Editing" states, but when i tap the button to change selectedType, the variable in the parent view does get updated, as I've observed in the debugger - however the view does not reload. So it appears as if the old filter is still being applied.

Is there any reason why updating this @State var is not reloading the view?

Are there any workarounds?

1
It is because selectedType is not used anywhere in body, calculated property filteredModels does not matter here. @State affects View only if it is used in this View body, otherwise it is just property.Asperi
@Asperi I thought it may be something like that... It is being used where im initializing the FilterBar - but i guess thats not good enough?Quinn
@Asperi is the solution to just add a pointless if that checks selectedType and is always true or something, so the view reloads?Quinn
Can you provide a git with minimum runnable code? (including all types you used)Mojtaba Hosseini
@Asperi I just tried the answer you gave me a couple days ago again, and it turns out you were right - it does work, i had something else that was making it seem like it didnt. You can repost it for the bounty if you see this.Quinn

1 Answers

2
votes

Well, it is like... workaround... but for testing, try

FilterBar(isEditing: $isEditing, selectedType: $selectedType)
if selectedType != .none {
   EmptyView()
}

In general, it would be correct to introduce view model as ObservableObject and have filteredModels in it as @Published, so your FilterBar changed that property, which will automatically refreshed the ParentView.