使用 ObservableObject 时视图不会更新

View doesn't get updated when using ObservableObject

我正在尝试使用 Swift 构建一个 Instagram 克隆应用程序UI。

我正在通过 Firebase 获取数据,并尝试在每次服务器中的数据发生变化时实现 UI 更新。

出于某种原因,当我第一次打开应用程序并获取数据时,我的视图的 body 被调用,但 UI 没有改变。我什至设置了一个断点,看到 body 被调用并包含正确的信息,只是 UI 没有得到更新。

我的应用程序中有几个选项卡,当我切换到另一个选项卡时(除了 Text 之外什么都没有),突然 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("Instagram", 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:

let firebaseData = 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)
                            }
                        }
                    }
                }
            }
        }
    }

    private func createPostFromDocument(document: QueryDocumentSnapshot) -> Post {
        let data = document.data()

        let id = document.documentID
        let imageUrl = data["imageUrl"] as! String
        let authorUsername = data["authorUsername"] as! String
        let authorProfilePictureUrl = data["authorProfilePictureUrl"] as! String
        let postLocation = data["postLocation"] as! String
        let postDescription = data["postDescription"] as! String
        let numberOfLikes = data["numberOfLikes"] as! Int
        let numberOfComments = data["numberOfComments"] as! Int
        let datePosted = (data["datePosted"] as! Timestamp).dateValue()
        let isLiked = data["isLiked"] as! Bool

        return Post(id: id, imageUrl: imageUrl, authorUsername: authorUsername, authorProfilePictureUrl: authorProfilePictureUrl, postLocation: postLocation, postDescription: postDescription, numberOfLikes: numberOfLikes, numberOfComments: numberOfComments, datePosted: datePosted, isLiked: isLiked)
    }
}

如果您需要我post更多代码,请告诉我。

更新:

Post查看:

struct PostView: View {
    @Binding var post: Post

    var body: some View {
        VStack(alignment: .leading) {
            //Info bar
            HStack {
                WebImage(url: URL(string: post.authorProfilePictureUrl))
                    .resizable()
                    .frame(width: 40, height: 40)
                    .clipShape(Circle())

                VStack(alignment: .leading, spacing: 2) {
                    Text(post.authorUsername).font(.headline)
                    Text(post.postLocation)
                }

                Spacer()

                Button(action: {
                    print("More options pressed")
                }, label: {
                    Image(systemName: "ellipsis")
                        .font(.title)
                        .foregroundColor(.black)
                }).buttonStyle(BorderlessButtonStyle())
            }
            .padding(.horizontal)

            //Main Image
            WebImage(url: URL(string: post.imageUrl))
              .resizable()
              .aspectRatio(contentMode: .fit)

            //Tools bar
            HStack(spacing: 15) {
                Button(action: {
                    self.post.isLiked.toggle()
                    print("Like btn pressed")
                }, label: {
                    Image(systemName: post.isLiked ? "heart.fill" : "heart")
                        .font(.title)
                        .foregroundColor(.black)
                }).buttonStyle(BorderlessButtonStyle())

                Button(action: {
                    print("Comments btn pressed")
                }, label: {
                    Image(systemName: "message")
                        .font(.title)
                        .foregroundColor(.black)
                }).buttonStyle(BorderlessButtonStyle())

                Button(action: {
                    print("Share btn pressed")
                }, label: {
                    Image(systemName: "paperplane")
                        .font(.title)
                        .foregroundColor(.black)
                }).buttonStyle(BorderlessButtonStyle())

                Spacer()

                Button(action: {
                    print("Bookmark btn pressed")
                }, label: {
                    Image(systemName: "bookmark")
                        .font(.title)
                        .foregroundColor(.black)
                }).buttonStyle(BorderlessButtonStyle())
            }.padding(8)

            Text("Liked by \(post.numberOfLikes) users")
                .font(.headline)
                .padding(.horizontal, 8)

            Text(post.postDescription)
                .font(.body)
                .padding(.horizontal, 8)
                .padding(.vertical, 5)

            Button(action: {
                print("Show comments btn pressed")
            }, label: {
                Text("See all \(post.numberOfComments) comments")
                    .foregroundColor(.gray)
                    .padding(.horizontal, 8)
            }).buttonStyle(BorderlessButtonStyle())

            Text(post.datePostedString)
                .font(.caption)
                .foregroundColor(.gray)
                .padding(.horizontal, 8)
                .padding(.vertical, 5)
        }
    }
}

Post:

struct Post : Identifiable, Hashable {
    var id: String
    var imageUrl: String
    var authorUsername: String
    var authorProfilePictureUrl: String
    var postLocation: String
    var postDescription: String
    var numberOfLikes: Int
    var numberOfComments: Int
    var datePostedString: String

    var isLiked: Bool

    init(id: String, imageUrl: String, authorUsername: String, authorProfilePictureUrl: String, postLocation: String, postDescription : String, numberOfLikes: Int, numberOfComments: Int, datePosted: Date, isLiked: Bool) {
        self.id = id
        self.imageUrl = imageUrl
        self.authorUsername = authorUsername
        self.authorProfilePictureUrl = authorProfilePictureUrl
        self.postLocation = postLocation
        self.postDescription = postDescription
        self.numberOfLikes = numberOfLikes
        self.numberOfComments = numberOfComments

        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "MMMM dd, yyyy"
        self.datePostedString = dateFormatter.string(from: datePosted)

        self.isLiked = isLiked
    }
}

谢谢!

问题是,当应用程序启动时,您的数组为空,并且 ScrollView 停止更新,您可以将其替换为 VStack,它会工作(仅用于测试)。

解决方案是用条件包裹 ForEach(或 ScrollView),如下所示:

if (fbData.posts.count > 0) {
    ForEach(self.fbData.posts.indices, id: \.self) { postIndex in
        PostView(post: self.$fbData.posts[postIndex])
            .listRowInsets(EdgeInsets())
            .padding(.vertical, 5)
    }
}