保存为UserDefaults,被调用19次

问题描述

嘿,由于状态更改或保存操作而多次调用某些SwiftUI代码是正常的吗?这段代码一个选择器,有4个选项可供选择。然后,选定的选项通过saveBase方法保存到UserDefaults中。我在方法添加了一条打印语句,以确认我的值正确到达,并发现每次对选择器进行更改时,该方法都会被调用19次。该代码可以很好地保存和还原baseCurr。作为具有汇编和C经验的固件工程师,我会认为这是错误代码,但是我不确定。有什么想法吗?

class UserData: ObservableObject {
    
    @Published var baseCurr: Int = 0

    func saveBase() -> () {
        let defaults = UserDefaults.standard
        defaults.set(self.baseCurr,forKey: "base")
        
        print("  base Curr = \(self.baseCurr)")
    }
}
struct aboutView: View {
    
    @EnvironmentObject var userData: UserData
    
    let baseCurrs = ["block A","block B","block C","block D"]
    
    var body: some View {
        
        Form {
            vstack (alignment: .leading) {
                Text("Select Base")
                
                Picker(selection: $userData.baseCurr,label: Text("Curr >")) {
                    ForEach(0 ..< baseCurrs.count) {
                        Text( baseCurrs[$0])
                    }
                    .onChange(of: userData.baseCurr) { newValue in
                        userData.saveBase()
                    }
                }
            }
            .padding()
        }
        .navigationBarTitle("About",displayMode: .inline) 
    }
}

解决方法

正如NewDev在评论中指出的那样,您需要将onChange修饰符附加到单个视图(例如PickerVStack)而不是ForEach

但是,您可以通过完全删除.onChange修饰符和saveBase()函数来使代码更简洁。

您可以将didSet添加到baseCurr属性中,以在设置 时保存该值:

class UserData: ObservableObject {
    @Published var baseCurr: Int = UserDefaults.standard.integer(forKey: "base") {
        didSet {
            UserDefaults.standard.set(baseCurr,forKey: "base")
            print("  base Curr = \(baseCurr)")
        }
    }
}

现在您不需要onChange

Picker(selection: $userData.baseCurr,label: Text("Curr >")) {
    ForEach(0 ..< baseCurrs.count) {
        Text(baseCurrs[$0])
    }
}
,

您的.onChange被调用baseCurrs.count次,因为您将其附加到了动态ForEach容器上,因此.onChange实际上附加到了每个Text(baseCurrs[$0])上,因此可以解决为此,保留您的代码,只需要将.onChange从动态容器中移出,例如

VStack (alignment: .leading) {
    Text("Select Base")
    
    Picker(selection: $userData.baseCurr,label: Text("Curr >")) {
        ForEach(0 ..< baseCurrs.count) {
            Text(baseCurrs[$0])
        }
    }
}
.onChange(of: userData.baseCurr) { newValue in   // for eg. here !!
    userData.saveBase()
}
.padding()