【问题标题】:Going crazy with UserDefaults in Swift[UI]在 Swift [UI] 中使用 UserDefaults 变得疯狂
【发布时间】:2020-07-30 20:46:04
【问题描述】:

开始使用 Swift 和 SwiftUI,我发现从 UIKit 迁移的过程非常困难。 目前被 UserDefaults 跺脚,即使在尝试理解我在网上找到的许多教程之后也是如此。

请告诉我我在这里做错了什么: 非常简单的代码:

  1. 向 UserDefault 注册一个 bool 值,
  2. 在文本中显示该布尔值!

没有比这更简单的了。 但我无法让它工作,因为对 UserDefaults 的调用会引发此错误消息:

实例方法'appendInterpolation'要求'Bool'符合'_FormatSpecifiable'

我的“应用程序”是默认的单视图应用程序,具有以下 2 项更改:

1- 在 AppDelegate 中,我注册了我的 bool:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.

    UserDefaults.standard.register(defaults: [
    "MyBool 1": true
    ])


    return true
}

2- 在 ContentView 中,我尝试显示它(在 struct ContentView: View 中):

let defaults = UserDefaults.standard

var body: some View {
    Text("The BOOL 1 value is : Bool 1 = \(defaults.bool(forKey: "MyBool 1"))")
}

有什么想法吗?

谢谢

【问题讨论】:

标签: swift swiftui userdefaults


【解决方案1】:

您的问题是 Text(...) 初始化程序采用 LocalizedStringKey 而不是 String ,它在字符串插值中支持与普通字符串不同的类型(显然不包括 Bool)。

有几种方法可以解决这个问题。

您可以使用带有StringText 初始化程序并逐字显示它而不尝试进行任何本地化:

var body: some View {
    Text(verbatim: "The BOOL 1 value is : Bool 1 = \(defaults.bool(forKey: "MyBool 1"))")
}

或者,您可以扩展 LocalizedStringKey.StringInterpolation 以支持布尔值,然后您的原始代码应该可以工作:

extension LocalizedStringKey.StringInterpolation {
    mutating func appendInterpolation(_ value: Bool) {
        appendInterpolation(String(value))
    }
}

【讨论】:

    【解决方案2】:

    要解决您的问题,只需添加描述变量,例如:

    var body: some View {
        Text("The BOOL 1 value is : Bool 1 = \(defaults.bool(forKey: "MyBool 1").description)")
    }
    

    【讨论】:

    • 您不应该使用description 属性。根据苹果文档:不鼓励直接调用此属性。相反,使用 String(describing:) 初始化器将任何类型的实例转换为字符串
    • 同意,您不应该使用 description 属性,使用 String(..) 请参阅下面的答案。
    • 取得进展!使用 String 包装器就像一个魅力。
    • @Rob 取得进展!使用 String 包装器就像一个魅力。我现在遇到了数据流问题。整个想法是使用 datePicker 并从 UserDefaults 日期实例化它,但选择器在重新启动应用程序时系统地将当前日期显示为启动器(杀死后)。我仍然通过 appDelegate 中的 register 方法实例化我的 UserDefaults,但在 UIKit 中,这不会覆盖用户未来的设置。这是我的完整代码:
    • 似乎无法在评论中发布我的代码...我想我得开始一个新话题了。
    【解决方案3】:

    回答您的问题:

    1- 将布尔值注册到 UserDefault,

    2- 在文本中显示该布尔值!

    我测试了以下代码,并确认它可以在 ios 13.4 和使用催化剂的 macos 上运行。注意 String(...) 包装。

    在 AppDelegate 类中

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
    //    UserDefaults.standard.register(defaults: ["MyBool 1": true])
       UserDefaults.standard.set(true, forKey: "MyBool 1")
        return true
    }
    

    在内容视图中

    import SwiftUI
    
    struct ContentView: View {
    
    @State var defaultValue = false   // for testing
    let defaults = UserDefaults.standard
    
    var body: some View {
        VStack {
            Text("bull = \(String(UserDefaults.standard.bool(forKey: "MyBool 1")))")
            Text(" A The BOOL 1 value is Bool 1 = \(String(defaultValue))")
            Text(" B The BOOL 1 value is : Bool 1 = \(String(defaults.bool(forKey: "MyBool 1")))")
        }
        .onAppear(perform: loadData)
    }
    
    func loadData() {
        defaultValue = defaults.bool(forKey: "MyBool 1")
        print("----> defaultValue: \(defaultValue) ")
    }
    }
    

    【讨论】:

      【解决方案4】:

      不知道你为什么使用register,但你可以像这样设置布尔值:

      UserDefaults.standard.set(true, forKey: "MyBool1")
      

      【讨论】:

      • 您好 Antoni,我使用 register 以便在首次启动时使用默认值填充 UserDefaults。用户稍后将覆盖它们...
      【解决方案5】:

      在 SwiftUI 中我使用这些:

      UserDefaults.standard.set(true, forKey: "MyBool 1")
      let bull = UserDefaults.standard.bool(forKey: "MyBool 1")
      

      【讨论】:

      • 你好 Workingdog。试过用这个,没有成功。尝试将 bool 设置为“onAppear”,没有乐趣。
      【解决方案6】:

      我认为使用 UserDefaults 的最佳方式是在类中。它可以帮助我们使用 @ObservedObject 属性包装器从任何模型订阅该类。

      布尔方法可用于types的其余部分

      //
      //  ContentView.swift
      //
      
      import SwiftUI
      
      struct ContentView: View {
          @ObservedObject var data = UserData()
          var body: some View {
              VStack {
                  Toggle(isOn: $data.isLocked){ Text("Locked") }
                  List(data.users) { user in
                      Text(user.name)
                      if data.isLocked {
                          Text("User is Locked")
                      } else {
                          Text("User is Unlocked")
                      }
                  }
              }
          }
      }
      
      
      //
      //  Model.swift
      //
      
      import SwiftUI
      import Combine
      
      let defaults = UserDefaults.standard
      let usersData: [User] = loadJSON("Users.json")
      // This is custom JSON loader and User struct is to be defined
      
      final class UserData: ObservableObject {
      
          // Saving a Boolean
      
          @Published var isLocked = defaults.bool(forKey: "Locked") {
              didSet {
                  defaults.set(self.isLocked, forKey: "Locked")
              }
          }
      
          // Saving Object after encoding
      
          @Published var users: [User] {
              // didSet will only work if used as Binding variable. Else need to create a save method, which same as the following didSet code.
              didSet {
                  // Encoding Data to UserDefault if value of user data change
                  if let encoded = try? JSONEncoder().encode(users) {
                      defaults.set(encoded, forKey: "Users")
                  }
              }
          }
          init() {
              // Decoding Data from UserDefault
              if let users = defaults.data(forKey: "Users") {
                  if let decoded = try? JSONDecoder().decode([User].self, from: users) {
                      self.users = decoded
                      return
                  }
              }
              // Fallback value if key "Users" is not found
              self.users = usersData
          }
          // resetting UserDefaults to initial values
          func resetData() {
              defaults.removeObject(forKey: "Users")
              self.isLocked = false
              self.users = usersData
          }
      }
      

      注意:此代码未经测试。在这里直接输入。

      【讨论】:

        【解决方案7】:

        试试这个:

        struct MyView {
            private let userDefaults: UserDefaults
        
            // Allow for dependency injection, should probably be some protocol instead of `UserDefaults` right away
            public init(userDefaults: UserDefaults = .standard) {
                self.userDefaults = userDefaults
            }
        }
        
        // MARK: - View
        extension MyView: View {
        
            var body: some View {
                Text("The BOOL 1 value is: \(self.descriptionOfMyBool1)")
            }
        }
        
        private extension MyView {
            var descriptionOfMyBool1: String {
                let key = "MyBool 1"
                return "\(boolFromDefaults(key: key))"
            }
        
            // should probably not be here... move to some KeyValue protocol type, that you use instead of `UserDefaults`...
            func boolFromDefaults(key: String) -> Bool {
                userDefaults.bool(forKey: key)
            }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-02-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多