切换选项卡后 UI 会随 ObservableObject 发生变化

Posted

技术标签:

【中文标题】切换选项卡后 UI 会随 ObservableObject 发生变化【英文标题】:UI changes with ObservableObject just after switching tabs 【发布时间】:2020-03-17 21:59:03 【问题描述】:

我有一个ObservableObject,用于在从服务器发送新数据时更新我的​​ UI(一个包含自定义结构数组的类)。

由于某种原因,发送数据时,ContentViewbody 被调用,但数据没有改变。我什至添加了一个print 语句来检查数组包含的数据是否正确。

当我尝试切换到 TabView 上的另一个选项卡,然后切换回主视图时,UI确实会更新。有人知道为什么 UI 会在我切换标签时更新,尽管 body 会在数据更改时被召回以更新 UI?

主视图

struct HomeView: View 
    @ObservedObject private var fbData = firebaseData

    var body: some View 
        TabView 
            //Home Tab
            NavigationView 
                ScrollView(showsIndicators: false) 
                    ForEach(self.fbData.posts.indices, id: \.self)  postIndex in
                        PostView(post: self.$fbData.posts[postIndex])
                            .listRowInsets(EdgeInsets())
                            .padding(.vertical, 5)
                    
                
                .navigationBarTitle("MyPhotoApp", displayMode: .inline)
                .navigationBarItems(leading:
                    Button(action: 
                        print("Camera btn pressed")
                    , label: 
                        Image(systemName: "camera")
                            .font(.title)
                    )
                , trailing:
                    Button(action: 
                        print("Messages btn pressed")
                    , label: 
                        Image(systemName: "paperplane")
                            .font(.title)
                    )
                )
             . tabItem(
                Image(systemName: "house")
                    .font(.title)
            )

            Text("Search").tabItem 
                Image(systemName: "magnifyingglass")
                    .font(.title)
            

            Text("Upload").tabItem 
                Image(systemName: "plus.app")
                    .font(.title)
            

            Text("Activity").tabItem 
                Image(systemName: "heart")
                    .font(.title)
            

            Text("Profile").tabItem 
                Image(systemName: "person")
                    .font(.title)
            
        
        .accentColor(.black)
        .edgesIgnoringSafeArea(.top)
    

FirebaseData

class FirebaseData : ObservableObject 
    @Published var posts = [Post]()

    let postsCollection = Firestore.firestore().collection("Posts")

    init() 
        self.fetchPosts()
    

    //MARK: Fetch Data
    private func fetchPosts() 
        self.postsCollection.addSnapshotListener  (documentSnapshot, err) in
            if err != nil 
                print("Error fetching posts: \(err!.localizedDescription)")
                return
             else 
                documentSnapshot!.documentChanges.forEach  diff in
                    if diff.type == .added 
                        let post = self.createPostFromDocument(document: diff.document)
                        self.posts.append(post)
                     else if diff.type == .modified 
                        self.posts = self.posts.map  (post) -> Post in
                            if post.id == diff.document.documentID 
                                return self.createPostFromDocument(document: diff.document)
                             else 
                                return post
                            
                        
                     else if diff.type == .removed 
                        for index in self.posts.indices 
                            if self.posts[index].id == diff.document.documentID 
                                self.posts.remove(at: index)
                            
                        
                    
                
            
        
    

【问题讨论】:

你能发布更多关于 ObservableObject 如何链接到 ContentView 的代码吗? 检查 ***.com/questions/60727556/… 并尝试了解 SwiftUI 如何知道要“刷新”、“重新加载”...等内容。您的数据和视图之间的关系是通过 '@State' 或 '@ ObservedObject 的属性包装器。 View 观察 State 或 ObservedObject 的内部,如果看到一些变化,它会重新评估其主体。仅此而已,背后没有奇迹。带有大量 .padding、.font .... 等的代码根本没有帮助。 @denis_lor 谢谢。我已经用更多信息更新了我的问题。 @user3441734 谢谢。我知道属性包装器是如何工作的,body 甚至可以正确回忆,但由于某种原因,只是 UI 本身没有改变。我已经添加了更多信息,如果你能看一下,我会很高兴 请发布您的 ObservableObject,这可能是问题的关键部分。 【参考方案1】:

您的代码示例无助于查找错误。最后我知道了如何演示它。首先,以“正确的方式”进行(复制-粘贴-自己尝试)

import SwiftUI

struct Data: Identifiable 
    let id = UUID()
    let text: String

class Model: ObservableObject 
    @Published var data: [Data] = [Data(text: "alfa"), Data(text: "beta")]
 

struct ContentView: View 
    @ObservedObject var model = Model()
    var body: some View 
        TabView 
            View1(model: model).tabItem 
                Text("View 1")
            
            View2(model: model).tabItem 
                Text("View 2")
            
        
    


struct View1: View 
    @ObservedObject var model: Model
    var body: some View 
        VStack 
            Text("View 1").font(.largeTitle)
            DataView(data: model.data)
            Button(action: 
                self.model.data.append(Data(text: String("ABCDEFGH".shuffled())))
            ) 
                Text("Add random data")
            
        
    

struct View2: View 
    @ObservedObject var model: Model
    var body: some View 
        VStack 
            Text("View 2").font(.largeTitle)
            DataView(data: model.data.filter( (item) -> Bool in
                item.text.count < 4
                ))
                // to distinguish from other DataView !! it seems to be a bug in SwiftUI
                // try to remove it to see the difference
                .id("view2")
            Button(action: 
                self.model.data.append(Data(text: String("ABC".shuffled())))
            ) 
                Text("Add random data")
            
        
    


struct DataView: View 
    var data: [Data]
    var body: some View 
        List 
            ForEach(data)  (item) in
                Text(item.text)
            
        
    


struct ContentView_Previews: PreviewProvider 
    static var previews: some View 
        ContentView()
    

我按原样工作,您可以修改每个选项卡中的数据,看到刷新的数据等。

移除“固定 - 用户定义” .id 修饰符,它会显着改变行为

这看起来像是 SwiftUI 中的一个严重错误 ...

【讨论】:

我想你不明白我所说的“当我尝试切换到 TabView 上的另一个选项卡,然后切换回主视图时,UI 确实会更新”的意思。我不想在 Tab1 到 Tab2 之间传递信息。 posts 数组应该在 tab1 中。但由于某种原因,tab1 没有得到更新,就在我切换到 tab2(它只有一个 text 标题,仅此而已)并切换回 tab1 时,信息更新。您发布的代码类似于我的代码,但添加了另一个选项卡。所以不幸的是它不起作用。还有其他想法吗? 在您发布一些最小的“工作”示例之前,我很抱歉……我在上面发布的是真正的错误,很难识别。我把它留给别人了…… 请在此处查看我更新的问题以及更多详细信息和代码:***.com/questions/60740197/…【参考方案2】:

我认为是 SwiftUI 错误。我这样解决这个问题。 而不是渲染你的 PostView(post: self.$fbData.posts[postIndex]) 在 ForEach 中实现 post 视图。

ForEach(self.fbData.posts.indices, id: \.self)  postIndex in
   Text(self.$fbData.posts[postIndex].comment)
   Text(self.$fbData.posts[postIndex].date)
   ....

【讨论】:

以上是关于切换选项卡后 UI 会随 ObservableObject 发生变化的主要内容,如果未能解决你的问题,请参考以下文章

切换选项卡后离子段不显示数据

android中的tabHost怎样在点击一个选项卡后跳转到一个activity,点击另一个选项卡跳转到另一个activity?

仅在键盘选项卡后获取焦点元素

更改选项卡后从链接禁用引导打开选项卡

创建 Honeycomb 操作栏选项卡后更改选项卡文本

选择另一个选项卡后,上一个选项卡选择保持活动状态