Reputation: 1233
I'm having an issue similar to the following post .onReceive firing twice.
I have a picker that fires .onChange
twice. I am using a model data Environment object for the picker.
Is there a way for me to get the before state such that I can compare if the new_haveCount
value is truely changing? Or better yet, to prevent the double fire in the first place?
@EnvironmentObject var modelData: ModelData
specifics
and specificsFirebase
are both structures.
Picker code
Picker(selection: $modelData.figureArray[figureIndex].specifics.specificsFirebase.new_haveCount,
label: Text(" \(modelData.figureArray[figureIndex].specifics.specificsFirebase.new_haveCount) ")) {
ForEach(0 ..< 20) {
Text("\(kSpecificType_Labels[1]) \($0) \(kNewText.lowercased())")
}
}
.onChange(of: modelData.figureArray[figureIndex].specifics.specificsFirebase.new_haveCount) { _ in
saveSpecifics()
}
From the apple dev page, .onChange
seems to have a before and ofter property.
struct PlayerView : View {
var episode: Episode
@State private var playState: PlayState = .paused
var body: some View {
VStack {
Text(episode.title)
Text(episode.showTitle)
PlayButton(playState: $playState)
}
.onChange(of: playState) { [playState] newState in
model.playStateDidChange(from: playState, to: newState)
}
}
}
Full View if it helps
import SwiftUI
import Firebase
struct SpecificsEntryView: View {
@EnvironmentObject var modelData: ModelData
let figure: Figure
var figureIndex: Int {
modelData.figureArray.firstIndex(where: { $0.id == figure.id })!
}
var body: some View {
HStack(spacing: 4) {
// new labels
VStack(alignment: .leading, spacing: 4) {
ForEach(kSpecificType_Labels, id: \.self) { label in
Text(label)
.frame(maxHeight: .infinity)
.padding(.bottom, 2)
Divider()
}
}
// new values
VStack(alignment: .center, spacing: 4) {
Text(kNewText)
.frame(maxHeight: .infinity)
.padding(.bottom, 2)
Divider()
Picker(selection: $modelData.figureArray[figureIndex].specifics.specificsFirebase.new_haveCount,
label: Text(" \(modelData.figureArray[figureIndex].specifics.specificsFirebase.new_haveCount) ")) {
ForEach(0 ..< 20) {
Text("\(kSpecificType_Labels[1]) \($0) \(kNewText.lowercased())")
}
}
.onChange(of: modelData.figureArray[figureIndex].specifics.specificsFirebase.new_haveCount) { _ in
saveSpecifics()
}
.frame(maxHeight: .infinity)
.padding(.bottom, 2)
.pickerStyle(MenuPickerStyle())
Divider()
Picker(selection: $modelData.figureArray[figureIndex].specifics.specificsFirebase.new_wantCount,
label: Text(" \(modelData.figureArray[figureIndex].specifics.specificsFirebase.new_wantCount) ")) {
ForEach(0 ..< 20) {
Text("\(kSpecificType_Labels[2]) \($0) \(kNewText.lowercased())")
}
}
.onChange(of: modelData.figureArray[figureIndex].specifics.specificsFirebase.new_wantCount) { _ in
saveSpecifics()
}
.frame(maxHeight: .infinity)
.padding(.bottom, 2)
.pickerStyle(MenuPickerStyle())
Divider()
Picker(selection: $modelData.figureArray[figureIndex].specifics.specificsFirebase.new_sellCount,
label: Text(" \(modelData.figureArray[figureIndex].specifics.specificsFirebase.new_sellCount) ")) {
ForEach(0 ..< 20) {
Text("\(kSpecificType_Labels[3]) \($0) \(kNewText.lowercased())")
}
}
.onChange(of: modelData.figureArray[figureIndex].specifics.specificsFirebase.new_sellCount) { _ in
saveSpecifics()
}
.frame(maxHeight: .infinity)
.padding(.bottom, 2)
.pickerStyle(MenuPickerStyle())
Divider()
Picker(selection: $modelData.figureArray[figureIndex].specifics.specificsFirebase.new_orderCount,
label: Text(" \(modelData.figureArray[figureIndex].specifics.specificsFirebase.new_orderCount) ")) {
ForEach(0 ..< 20) {
Text("\(kSpecificType_Labels[4]) \($0) \(kNewText.lowercased())")
}
}
.onChange(of: modelData.figureArray[figureIndex].specifics.specificsFirebase.new_orderCount) { _ in
saveSpecifics()
}
.frame(maxHeight: .infinity)
.padding(.bottom, 2)
.pickerStyle(MenuPickerStyle())
Divider()
} // end new vstack
Divider() // vertical
// loose values
VStack(alignment: .center, spacing: 4) {
Text(kLooseText)
.frame(maxHeight: .infinity)
.padding(.bottom, 2)
Divider()
Picker(selection: $modelData.figureArray[figureIndex].specifics.specificsFirebase.loose_haveCount,
label: Text(" \(modelData.figureArray[figureIndex].specifics.specificsFirebase.loose_haveCount) ")) {
ForEach(0 ..< 20) {
Text("\(kSpecificType_Labels[1]) \($0) \(kNewText.lowercased())")
}
}
.onChange(of: modelData.figureArray[figureIndex].specifics.specificsFirebase.loose_haveCount) { _ in
saveSpecifics()
}
.frame(maxHeight: .infinity)
.padding(.bottom, 2)
.pickerStyle(MenuPickerStyle())
Divider()
Picker(selection: $modelData.figureArray[figureIndex].specifics.specificsFirebase.loose_wantCount,
label: Text(" \(modelData.figureArray[figureIndex].specifics.specificsFirebase.loose_wantCount) ")) {
ForEach(0 ..< 20) {
Text("\(kSpecificType_Labels[2]) \($0) \(kNewText.lowercased())")
}
}
.onChange(of: modelData.figureArray[figureIndex].specifics.specificsFirebase.loose_wantCount) { newVal in
print("\(modelData.figureArray[figureIndex].specifics.specificsFirebase.loose_wantCount) to \(newVal)")
saveSpecifics()
}
.frame(maxHeight: .infinity)
.padding(.bottom, 2)
.pickerStyle(MenuPickerStyle())
Divider()
Picker(selection: $modelData.figureArray[figureIndex].specifics.specificsFirebase.loose_sellCount,
label: Text(" \(modelData.figureArray[figureIndex].specifics.specificsFirebase.loose_sellCount) ")) {
ForEach(0 ..< 20) {
Text("\(kSpecificType_Labels[3]) \($0) \(kNewText.lowercased())")
}
}
.onChange(of: modelData.figureArray[figureIndex].specifics.specificsFirebase.loose_sellCount) { _ in
saveSpecifics()
}
.frame(maxHeight: .infinity)
.padding(.bottom, 2)
.pickerStyle(MenuPickerStyle())
Divider()
TextField("Order from", text: $modelData.figureArray[figureIndex].specifics.specificsFirebase.new_orderText,
onCommit: {
saveSpecifics()
})
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading)
.padding(.bottom, 2)
.background(Color(.systemGray5))
.cornerRadius(4)
Divider()
} // end loose vstack
} // end all hstack specifics
.fixedSize(horizontal: false, vertical: true)
.font(/*@START_MENU_TOKEN@*/.title/*@END_MENU_TOKEN@*/)
} // end body
// save specifics on update
func saveSpecifics() {
// Inject Firebase authentication
let userID = Auth.auth().currentUser?.uid
modelData.figureArray[figureIndex].specifics.specificsFirebase.saveSpecifics(userID: userID!)
}
}
Upvotes: 3
Views: 1379
Reputation: 1020
I was having a similar issue, but I was getting 4 duplicate .onChange
handler calls on a DatePicker control.
I ended up changing my code to use the Binding extension from here and the duplicate calls went away https://www.hackingwithswift.com/quick-start/swiftui/how-to-run-some-code-when-state-changes-using-onchange
I'd still like to know what was causing the duplicate calls if someone has any insight.
Upvotes: 0
Reputation: 41
I ran into the same capture list syntax issue and fixed it by using an explicit alias for the captured value from the EnvironmentObject variable (Xcode gave a hint). Like so:
.onChange(model.someVariable) {[oldValue = model.someVariable] newValue in { ... }
But my code still causes .onChange
to fire twice...
Upvotes: 2