保存到 UserDefaults 被调用 19 次

Posted

技术标签:

【中文标题】保存到 UserDefaults 被调用 19 次【英文标题】:Save to UserDefaults Getting Called 19 Times 【发布时间】:2020-10-13 22:19:01 【问题描述】:

嘿,由于状态更改或保存操作,某些 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) 
    

【问题讨论】:

.onAppear 附加到PickerVStack 而不是ForEach 【参考方案1】:

正如 NewDev 在 cmets 中指出的那样,您需要将 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])
    

【讨论】:

我没有展示它,但是 UserData init 方法从 UserDefaults 初始化 baseCurr。那么你认为 iPhone 中缺少信号(输入)去抖动吗?嘈杂的选择器导致 onChange 多次调用 save 方法? @GalenSmith 您只是不能将onChange 附加到 ForEach。请从我的回答中查看UserData 版本。 我摆脱了 onChange 并将您建议的更改添加到 UserData。它工作得很好,只更新一次 baseCurr! @GalenSmith 很高兴它对您有所帮助 :) 如果您觉得我的回答解决了您的问题,您可以接受(和/或投票) - 请参阅 What should I do when someone answers my question?。【参考方案2】:

您的.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()

【讨论】:

以上是关于保存到 UserDefaults 被调用 19 次的主要内容,如果未能解决你的问题,请参考以下文章

使用 struct id 将收藏夹保存到 UserDefaults

UserDefaults不保存值iphone

Swift - UserDefaults 设置未保存在框架内

什么是使用UserDefaults保存数据的适当事件?

每次调用 .insert() 或 .remove(at:) 时,在 UserDefaults 中保存更新的 Set

UserDefaults with Struct - TableView 不显示从 UserDefaults 保存的数据?