如何在SwiftUI中使用UserDefaults?

Smu*_*aco 7 nsuserdefaults swift swiftui

struct ContentView: View {
@State var settingsConfiguration: Settings
    struct Settings {
        var passwordLength: Double = 20
        var moreSpecialCharacters: Bool = false
        var specialCharacters: Bool = false
        var lowercaseLetters: Bool = true
        var uppercaseLetters: Bool = true
        var numbers: Bool = true
        var space: Bool = false
    }
  var body: some View {
    VStack {
                HStack {
                    Text("Password Length: \(Int(settingsConfiguration.passwordLength))")
                    Spacer()
                    Slider(value: $settingsConfiguration.passwordLength, from: 1, through: 512)
                }
                Toggle(isOn: $settingsConfiguration.moreSpecialCharacters) {
                    Text("More Special Characters")
                }
                Toggle(isOn: $settingsConfiguration.specialCharacters) {
                    Text("Special Characters")
                }
                Toggle(isOn: $settingsConfiguration.space) {
                    Text("Spaces")
                }
                Toggle(isOn: $settingsConfiguration.lowercaseLetters) {
                    Text("Lowercase Letters")
                }
                Toggle(isOn: $settingsConfiguration.uppercaseLetters) {
                    Text("Uppercase Letters")
                }
                Toggle(isOn: $settingsConfiguration.numbers) {
                    Text("Numbers")
                }
                Spacer()
                }
                .padding(.all)
                .frame(width: 500, height: 500)
  }
}
Run Code Online (Sandbox Code Playgroud)

因此,我在这里拥有所有这些代码,我想使用UserDefaults来保存设置,无论何时更改开关或滑动滑块,并在应用启动时检索所有这些数据,但是我不知道如何在SwiftUI中使用UserDefaults (或者说是UserDefaults,通常我才开始研究它,以便可以在SwiftUI应用程序中使用它,但是我看到的所有示例都是针对UIKit的,当我尝试在SwiftUI中实现它们时,我会遇到很多错误)。

Ser*_*iiK 28

从 Xcode 12.0 (iOS 14.0) 开始,您可以@AppStorage对以下类型使用属性包装器:Bool, Int, Double, String, URLData. 以下是存储字符串值的用法示例:

struct ContentView: View {
    
    static let userNameKey = "user_name"
    
    @AppStorage(Self.userNameKey) var userName: String = "Unnamed"
    
    var body: some View {
        VStack {
            Text(userName)
            
            Button("Change automatically ") {
                userName = "Ivor"
            }
            
            Button("Change manually") {
                UserDefaults.standard.setValue("John", forKey: Self.userNameKey)
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在这里,您userName使用默认值声明属性,该属性不会归于UserDefaults自身。当您第一次改变它时,应用程序会将该值写入UserDefaults并使用新值自动更新视图。

UserDefaults如果需要,还可以通过如下store参数设置自定义提供程序:

@AppStorage(Self.userNameKey, store: UserDefaults.shared) var userName: String = "Mike"
Run Code Online (Sandbox Code Playgroud)

extension UserDefaults {
    static var shared: UserDefaults {
        let combined = UserDefaults.standard
        combined.addSuite(named: "group.myapp.app")
        return combined
    }
}
Run Code Online (Sandbox Code Playgroud)

注意:如果该值将在应用程序之外更改(假设手动打开 plist 文件并更改值),View 将不会收到该更新。

PS 还有一个新的扩展Viewfunc defaultAppStorage(_ store: UserDefaults) -> some View它允许更改用于视图的存储。如果有很多@AppStorage属性并且为每个属性设置自定义存储很麻烦,这会很有帮助。

  • 我不同意。这是个好地方,你的评论已经过时了 (11认同)
  • 还可以将“@AppStorage”与其他类型(如“Array”)一起使用 - 请参阅[此答案](/sf/answers/4379464141/) (2认同)

pro*_*asm 24

下面的代码改编了 Mohammad Azam 在此视频中的出色解决方案:

import SwiftUI

struct ContentView: View {
    @ObservedObject var userDefaultsManager = UserDefaultsManager()

    var body: some View {
        VStack {
            Toggle(isOn: self.$userDefaultsManager.firstToggle) {
                Text("First Toggle")
            }

            Toggle(isOn: self.$userDefaultsManager.secondToggle) {
                Text("Second Toggle")
            }
        }
    }
}

class UserDefaultsManager: ObservableObject {
    @Published var firstToggle: Bool = UserDefaults.standard.bool(forKey: "firstToggle") {
        didSet { UserDefaults.standard.set(self.firstToggle, forKey: "firstToggle") }
    }

    @Published var secondToggle: Bool = UserDefaults.standard.bool(forKey: "secondToggle") {
        didSet { UserDefaults.standard.set(self.secondToggle, forKey: "secondToggle") }
    }
}
Run Code Online (Sandbox Code Playgroud)


Mar*_* T. 10

通常,使用caram的方法是可以的,但是代码存在很多问题,SmushyTaco无法使其正常工作。在下面,您将找到“开箱即用”的工作解决方案。

1. UserDefaults propertyWrapper

import Foundation

@propertyWrapper
struct UserDefault<T> {
    let key: String
    let defaultValue: T

    init(_ key: String, defaultValue: T) {
        self.key = key
        self.defaultValue = defaultValue
    }

    var wrappedValue: T {
        get {
            return UserDefaults.standard.object(forKey: key) as? T ?? defaultValue
        }
        set {
            UserDefaults.standard.set(newValue, forKey: key)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

2. UserSettings类

final class UserSettings: ObservableObject {

    let objectWillChange = PassthroughSubject<Void, Never>()

    @UserDefault("ShowOnStart", defaultValue: true)
    var showOnStart: Bool {
        willSet {
            objectWillChange.send()
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

3. SwiftUI视图

struct ContentView: View {

@ObservedObject var settings = UserSettings()

var body: some View {
    VStack {
        Toggle(isOn: $settings.showOnStart) {
            Text("Show welcome text")
        }
        if settings.showOnStart{
            Text("Welcome")
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 你真的需要定义“objectWillChange”吗?它会自动合成。 (3认同)
  • 不要忘记“导入组合”,否则你会得到错误。 (2认同)
  • 我使用相同的代码,但是当我在开始到结束时设置 UserDefault Value 时,它​​是相同的。我更新了值,但 UserDefault 中的值没有发生变化。 (2认同)

car*_*ram 7

首先,创建一个属性包装器,让我们可以轻松地在您的 Settings 类和 UserDefaults 之间建立链接:

import Foundation

@propertyWrapper
struct UserDefault<Value: Codable> {    
    let key: String
    let defaultValue: Value

    var value: Value {
        get {
            let data = UserDefaults.standard.data(forKey: key)
            let value = data.flatMap { try? JSONDecoder().decode(Value.self, from: $0) }
            return value ?? defaultValue
        }
        set {
            let data = try? JSONEncoder().encode(newValue)
            UserDefaults.standard.set(data, forKey: key)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,创建一个保存您的设置的数据存储:

import Combine
import SwiftUI

final class DataStore: BindableObject {
    let didChange = PassthroughSubject<DataStore, Never>()

    @UserDefault(key: "Settings", defaultValue: [])
    var settings: [Settings] {
        didSet {
            didChange.send(self)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,在您看来,访问您的设置:

import SwiftUI

struct SettingsView : View {
    @EnvironmentObject var dataStore: DataStore

    var body: some View {
        Toggle(isOn: $settings.space) {
            Text("\(settings.space)")
        }
    }
}
Run Code Online (Sandbox Code Playgroud)