SwiftUI 中的领域:UI 中仅偶尔保存对象的实时文本

Posted

技术标签:

【中文标题】SwiftUI 中的领域:UI 中仅偶尔保存对象的实时文本【英文标题】:Realm in SwiftUI: Live Text in UI for Object Only Saved Occasionally 【发布时间】:2021-04-27 01:40:49 【问题描述】:

我在一个 SwiftUI 应用程序中比较 Core Data 和 Realm,Core Data 做了一些我希望弄清楚如何在 Realm 中做的事情。

Core Data 让您可以随时更改对象,当它们在 SwiftUI 中为 ObservableObject 时,您的 UI 会立即更新。然后,只要您想保留更改,就保存 context

在 Realm 中,UI 中的对象是实时的,但除非您处于写入事务中,否则您无法更改它们。当实际的write 只是偶尔执行时,我试图让我的 UI 反映用户的实时/即时更改。下面是一个示例应用程序。

这是我的 Realm 模型:

import RealmSwift

class Item: Object, ObjectKeyIdentifiable
  @objc dynamic var recordName = ""
  @objc dynamic var text = ""

  override class func primaryKey() -> String? 
    return "recordName"
  

这是我的视图模型,其中还包括我的 save() 函数,该函数仅每 3 秒保存一次。在我的实际应用中,这是因为这是一项昂贵的操作,并且在用户键入时执行此操作会使应用陷入爬行状态。

class ViewModel: ObservableObject
  static let shared = ViewModel()
  @Published var items: Results<Item>!
  @Published var selectedItem: Item?
  
  var token: NotificationToken? = nil
  
  init()
    //Add dummy data
    let realm = try! Realm()
    realm.beginWrite()
    
    let item1 = Item()
    item1.recordName = "one"
    item1.text = "One"
    realm.add(item1, update: .all)
    
    let item2 = Item()
    item2.recordName = "two"
    item2.text = "Two"
    realm.add(item2, update: .all)
    
    try! realm.commitWrite()
    
    self.fetch()
    
    //Notifications
    token = realm.objects(Item.self).observe [weak self] _ in
      self?.fetch()
    
  
  
  //Get Data
  func fetch()
    let realm = try! Realm()
    self.items = realm.objects(Item.self)
  
  
  //Save Data
  var saveTimer: Timer?
  func save(item: Item, text: String)
    //Save occasionally
    saveTimer?.invalidate()
    saveTimer = Timer.scheduledTimer(withTimeInterval: 3, repeats: false) _ in
      
      let realm = try! Realm()
      try? realm.write(
        item.text = text
      )
      
    
  
  

最后,这是用户界面。它非常基本,反映了我试图实现这一点的应用程序的一般结构。

struct ContentView: View 
  @StateObject var model = ViewModel.shared
  
  var body: some View 
   
    VStack
      ForEach(model.items) item in
        HStack
          Button(item.text)
            model.selectedItem = item
          
          Divider()
          ItemDetail(item: item)
        
      
    

  

...以及 ItemDetail 视图:

struct ItemDetail: View
  @ObservedObject var item: Item
  @StateObject var model = ViewModel.shared
  
  init(item: Item)
    self.item = item
  
  
  var body: some View
    //Binding
    let text = Binding<String>(
      get:  item.text ,
      set:  model.save(item: item, text: $0) 
    )
    
    TextField("Text...", text: text)
      .textFieldStyle(RoundedBorderTextFieldStyle())
  

当我输入TextField 时,我如何让Button 文本反映我输入的内容实时考虑到我的realm.write 只发生每 3秒?我的Button 在写入后更新,但我希望 UI 能够实时响应——独立于写入。

【问题讨论】:

这是多用户应用还是单用户应用?此外,尚不清楚您为什么要获取对象,并且当发生更改时,您会再次获取它们。更改将自动反映在对象中,what 更改将反映在通知中。你为什么每 3 秒写一次 Realm?是什么阻止您在用户键入时使用字段中的文本简单地更新按钮文本?根本不需要领域。 这是一个多用户应用程序(虽然我没有使用 Realm Sync,如果这就是你所指的)。 当有更改时我会再次获取,因为它比处理单个更改更简单。 我每隔几秒钟就写一次,因为输入的文本会被处理并转换为 Base64 并且每次击键都非常慢(我必须等待几秒钟才能显示我键入的每个字母) . 如果我不需要 Realm 来反映更新的文本,那就太好了。但我不清楚如何。我是否设置了绑定并以某种方式将其值与保存到 Realm 的值相协调?我的问题的要点是如何做这样的事情。 【参考方案1】:

根据Jay 的建议文档,我得到了以下工作,这要简单得多:

我的主视图添加了 @ObservedResults 属性包装器,如下所示:

struct ContentView: View 
  @StateObject var model = ViewModel.shared
  @ObservedResults(Item.self) var items

  var body: some View 
   
    VStack
      ForEach(items) item in
        HStack
          Button(item.text)
            model.selectedItem = item
          
          Divider()
          ItemDetail(item: item)
        
      
    

  

...然后ItemDetail 视图只使用@ObservedRealmObject 属性包装器,它绑定到 Realm 中的值并自动管理写入:

struct ItemDetail: View
  @ObservedRealmObject var item: Item

  var body: some View
    TextField("Text...", text: $item.text)
      .textFieldStyle(RoundedBorderTextFieldStyle())
  

这本质上是 Core Data 的工作方式(就视图代码而言),但 Realm 会自动保存到存储中。谢谢你,杰!

【讨论】:

天哪!好样的!非常高兴您有解决方案。 我要补充一点,这在我设计的示例中非常有效,但我在更复杂的实现中遇到了两件事:(1) 我宁愿在其中定义我的 Results,并且从视图模型中读取。 (2) 如何使用.filter().sorted()@ObservedResults 执行更复杂的查询?

以上是关于SwiftUI 中的领域:UI 中仅偶尔保存对象的实时文本的主要内容,如果未能解决你的问题,请参考以下文章

Firebase 引用对象不会保存到 SwiftUI 中的变量

当我绑定它的值时,SwiftUI TextField 的行为很奇怪(不能输入中文,偶尔退格会跳过一个字符)

核心数据对象仍然保存在presentationMode SwiftUI中的dismiss/cancel

Swift UI:使用数组中的随机元素更新视图

如何让用户在 SwiftUI 文本字段中仅使用数字输入货币,同时保留 $ 和 .?

SwiftUI - 环境对象未更新 UI