I am trying to bind a Picker, but it's not working. I create a BindableObject, an instance of Settings, add it to the environment using environmentObject() in SceneDelegate, and address it in the View using @EnvironmentObject
struct ContentView : View {
var favoriteFoods = ["Tofu", "Seitan", "Nilla Wafers", "Avocado Toast"]
@EnvironmentObject var settings : Settings
// meanwhile, inside var body: some View ...
Picker(selection: $settings.favoriteFoodChoice, label: Text("Favorite Food")){
ForEach(self.favoriteFoods.identified(by: \.self)){ food in
Text(food)
}
}
Here's what my Settings looks like:
class Settings : BindableObject {
var didChange = PassthroughSubject<void,never>()
var favoriteFoodChoice:Int {
willSet {
print("Favorite Food Choice will be \(newValue)")
didChange.send()
print("Favorite Food Choice: \(favoriteFoodChoice)")// never changes when I select a different food.
}
didSet {
print("Favorite Food Choice was \(oldValue)")
didChange.send()
print("Favorite Food Choice: \(favoriteFoodChoice)")// never changes when I select a different food.
}
}
init(favoriteFoodChoice:Int) {
self.favoriteFoodChoice = favoriteFoodChoice
}
convenience init(){
self.init(favoriteFoodChoice:0)
}
}
When I change the selected food, I see output from print(), but favoriteFoodChoice stays the same. Isn't it supposed to change?? Or am I misunderstanding how binding works with Pickers?
Any help would be appreciated!
I should be embarrassed to post from memory, since I am unable to test code (posting from my phone), but I think if you remove your event handler (didChange) for willSet, and use only didSet, you'll get your expected result. didChange, as I understand it, is the notification to swiftUI to update the environment...there may be something wonky in sending didChange before and after a data change, but I'm unsure, as it's hard to peek under the hood of some of the less documented stuff.
edit:
Here is your code... modified to function as I think you expect:
struct ContentView: View {
var favoriteFoods = ["Tofu", "Seitan", "Nilla Wafers", "Avocado Toast"]
@EnvironmentObject var settings: Settings
var body: some View {
HStack {
Text("Picker: ")
Picker(selection: $settings.favoriteFoodChoice, label: Text("Favorite Food")){
ForEach(0 ..< favoriteFoods.count) {
Text(self.favoriteFoods[$0]).tag($0)
}
}
}
}
}
class Settings: BindableObject {
var didChange = PassthroughSubject<void, never="">()
var favoriteFoodChoice: Int = 0 {
willSet {
print("Favorite Food Choice will be \(newValue)")
//didChange.send()
print("Favorite Food Choice: \(favoriteFoodChoice)") // never changes when I select a different food.
}
didSet {
print("Favorite Food Choice was \(oldValue)")
didChange.send()
print("Favorite Food Choice: \(favoriteFoodChoice)") // never changes when I select a different food.
}
}
}
I killed the extra didChange notification, and corrected the syntax of your Picker control to show labels in the picker control (and shortend the syntax of the ForEach loop...just to see if I could remember how, I'm novice with Swift). I also moved the variable init into its declaration and removed the two init() calls, in essence setting the control to the first available selection.