I feel like I must be doing something stupid. All I’m trying to do if pull from UserDefaults to set a Picker value, then save to UserDefaults when the Picker changes. The issue I’m running into comes when I try and make the code work for iOS14 and 15.
struct SettingsView: View {
@State var rangeSelection: Int = UserDefaults.standard.integer(forKey: "vocalRange")
@State var rangeOptions = ["Soprano", "Mezzo-Soprano/Alto", "Extended Range"]
var body: some View {
VStack{
Form {
Section {
Picker(selection: $rangeSelection, label: Text("Range")) {
ForEach(0 ..< rangeOptions.count, id: .self) { range in
Text(rangeOptions[range])
.tag(range)
}
}
.onChange(of: rangeSelection, perform: { value in
UserDefaults.standard.setValue(rangeSelection, forKey: "vocalRange")
})
}
}
}
.navigationTitle("Settings")
.navigationBarTitleDisplayMode(.inline)
}
}
This seems to work fine, and the ‘onChange’ fires great. Only weird thing is, if you leave the page and immediately go back in, the ‘rangeSelection’ variable resets to what it was originally. Once you restart the app however, the change is reflected correctly.
As an alternative, I tried the following
@State var rangeSelection: Int = 0
...
.onAppear(perform:{
rangeSelection = UserDefaults.standard.integer(forKey: "vocalRange")
})
This creates another fun issue. In iOS15 it works fine, but in iOS14, once you’ve changed the picker, the OnAppear fires before the OnChange, and reverts the Picker back to its previous value. This only happens because I’m using the picker in a Form/List, so it changes pages when you select the Picker.
I feel like there is an obvious solution I should be using.
2
Answers
Thanks to @loremipsum I used AppStorage. I declared the variable like this, and no 'onChange' was needed!
You can set delay in .OnAppear method to work it