SwiftUI - 使用 @ObservedObject 作为同一结构中 @FetchRequest 的结果来强制更新 UI

Posted

技术标签:

【中文标题】SwiftUI - 使用 @ObservedObject 作为同一结构中 @FetchRequest 的结果来强制更新 UI【英文标题】:SwiftUI - Using an @ObservedObject for the result of an @FetchRequest in the same struct to force-update the UI 【发布时间】:2021-08-13 20:27:37 【问题描述】:

据我所知,我将 Core Data Entity 对象传递给子结构中的 @ObservedObject,以在我更改 Core Data 对象时强制 UI 更新。

例子:

struct ParentView: View 

@FetchRequest(entity: CDUser.entity(), sortDescriptors: [])
var users: FetchedResults<CDUser>

   var body: some View 
      List 
         ForEach(users, id: \.self)  item in 
            ChildView(item: item)
         
      
   



struct ChildView: View 

@Environment(\.managedObjectContext) private var moc
@ObservedObject var item: CDUser

   var body: some View 
      HStack 
         Button(action: 
               item.count = item.count + 1
               do 
                  try moc.save()
                catch 
                  print(error)
               
            , label: 
               Text(String(item.count))
            
         )
      
   

@ObservedObject 将强制 UI 在按下按钮时显示增加的 item.count。

虽然这很好用,但有没有办法通过将 @FetchRequest 和 @ObservedObject 放在同一个结构中来不使用父子层次结构来强制 UI 更新? 理想情况下,我只有一个结构,其中包含按钮。

下面的示例将更新代码数据对象,但更改只会在视图重新出现时反映在 UI 中。

例子:

struct MainView: View 

@Environment(\.managedObjectContext) private var moc

@FetchRequest(entity: CDUser.entity(), sortDescriptors: [])
var users: FetchedResults<CDUser>

// HOW DO I INTEGRATE THIS OBSERVED OBJECT TO REFLECT THE BUTTON ACTION IMMEDIATELY IN THE UI?
// @ObservedObject var users: [CDUser]
//

   var body: some View 
      List 
         ForEach(users, id: \.self)  item in 
            Button(action: 
                  item.count = item.count + 1
                  do 
                     try moc.save()
                   catch 
                     print(error)
                  
               , label: 
                  Text(String(item.count))
               
            )
         
      
   

编辑:添加一个更复杂的示例,其中将按钮更改为子视图不会导致部分更新:

struct MainView: View 

    @Environment(\.managedObjectContext) private var moc

    @FetchRequest(entity: CDUser.entity(), sortDescriptors: [])
    var users: FetchedResults<CDUser>
    
    var body: some View 
        List 
            //Section of logged-in users
            Section(header: Text("Logged in")) 
                ForEach(users.filter( $0.logged == true ), id: \.self)  user in
                    Button(action: 
                        user.logged = false
                        do 
                            try moc.save()
                         catch 
                            print(error)
                        
                    , label: 
                        Text(String(user.firstName))
                    )
                
                //Section of logged-out users
                Section(header: Text("Logged out")) 
                    ForEach(users.filter( $0.logged == false ), id: \.self)  user in
                        Button(action: 
                            user.logged = true
                            do 
                                try moc.save()
                             catch 
                                print(error)
                            
                        , label: 
                            Text(String(user.firstName))
                        )
                    
                
            
        
    

【问题讨论】:

你不能那样做。原因很简单,数组不是 ObservableObject。制作一个 CDUserButtonView 以便您可以观察循环中的项目 ***.com/questions/68710726/swiftui-view-updating/… 完全同意使用 ObservedObject 创建子视图适用于初始示例。我在问题中添加了一个更复杂的示例,其中子视图策略不起作用,因为列表基于 FetchRequest 而不是 ObservedObject。您将如何解决这个问题,因为我无法将 FetchedResult 数组传递给子视图的 ObservedObject? @loremipsum 什么部分没有更新?项目从一个列表移动到另一个列表?那是因为您应该使用 nspredicate 与没有任何内置观察者的过滤器,这是预期的行为,当整个数组发生变化时,获取结果只会导致重绘。数组不能是观察对象,只能单独观察数组的元素 @Peanutsmasher 老实说,它看起来更加整洁,因为它已经是。您应该尝试将视图拆分为尽可能小的视图,而不是将所有视图放在一起。我不认为有什么应该改变的。复杂的 SwiftUI 视图变成了pyramid of doom。 【参考方案1】:

你不能这样做。因为数组不是ObservableObject。创建一个CDUserButtonView,以便您可以观察循环中的项目

您添加的部分不会更新,因为您应该使用没有任何内置观察器的 NSPredicatefilter,这是预期的行为,FetchResults 仅在数组时导致重绘整体变化。数组不能是观察对象,只能单独观察数组的元素。

此外,如果您的目标是 ios 15+,您应该查看SectionedFetchRequest,它会为您创建这些部分。

https://developer.apple.com/wwdc21/10017

大约是 23:26 分

【讨论】:

以上是关于SwiftUI - 使用 @ObservedObject 作为同一结构中 @FetchRequest 的结果来强制更新 UI的主要内容,如果未能解决你的问题,请参考以下文章

如何在 SwiftUI 中使用 Realm

使用 SwiftUI 实现应用内购买 [关闭]

SwiftUI 使用SwiftUI实现跑马灯效果 Marquee

SwiftUI 使用SwiftUI实现跑马灯效果 Marquee

SwiftUI 使用SwiftUI实现跑马灯效果 Marquee

SwiftUI 使用SwiftUI实现跑马灯效果 Marquee