UI changes with ObservableObject just after switching tabs

烈酒焚心 提交于 2021-02-11 16:51:24

问题


I have a ObservableObject that I use to update my UI when new data is sent from the server (an a class which contains an array of custom structs).

For some reason, when the data is sent, the ContentView's body is called, but the data isn't changed. I even added a print statement to check if the data that the array contains is right and it is.

When I try to switch to another tab on my TabView, and then switch back to the main view, the UI does get updated. Does anybody know why the UI updates just when I switch tabs, although the body gets recalled to update the UI when the data changed?

HomeView

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)
                            }
                        }
                    }
                }
            }
        }
    }

回答1:


Your code example doesn't help to find the bug. Finally I've got how to demonstrate it. First, do it the "proper way" (copy - paste - try it yourself)

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()
    }
}

I works as it should, you can modify the data from each tab, you see refreshed data, etc.

removing "fixed - user defined" .id modifier, it changes the behaviour dramatically

This looks like a serious bug in SwiftUI ...




回答2:


I think is SwiftUI bug. I solve this problem like this. Instead of rendering your PostView(post: self.$fbData.posts[postIndex]) implement post view inside ForEach.

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


来源:https://stackoverflow.com/questions/60730835/ui-changes-with-observableobject-just-after-switching-tabs

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!