As far as I understand, if I need to update the view model from inside a view, I need to make it a binding variable.
The model.
enum Options: Int, Identifiable, Equatable, CaseIterable {
case option1 = 1
case option2 = 2
case option3 = 3
case option4 = 4
var id: String { "\(self.rawValue)" }
}
class TestViewModel: ObservableObject {
var selectedOption = Options.option1
...
}
The view.
struct TestView: View {
@Binding var viewModel: TestViewModel
@State var selectedOption = Options.option1
var body: some View {
Picker("Option 1", selection: $viewModel.selectedOption) {
ForEach(Options.allCases, id: \.id) { value in
Text(value.id)
.tag(value)
}
}
Text("Selected Option: \(viewModel.selectedOption.rawValue)")
Picker("Option 2", selection: $selectedOption) {
ForEach(Options.allCases, id: \.id) { value in
Text(value.id)
.tag(value)
}
}
Text("Selected Option: \(selectedOption.rawValue)")
}
}
- Selecting a value in the first picket, the view doesn't refresh.
- Selecting a value in the second picker, the view refreshes (as expected)
How can I make the view refresh using @Binding which is required to update the model?
I come up with this solution which works but it doesn't look good to me.
...
let b = Binding<Options>(
get: {
viewModel.selectedOption
},
set: {
viewModel.selectedOption = $0
selectedOption = $0 // << this forces the view to refresh
}
)
Picker("Speed 1", selection: b) {
ForEach(Options.allCases, id: \.id) { value in
Text(value.id)
.tag(value)
}
}
...