简体   繁体   English

使用 ObservableObject 时视图不会更新

[英]View doesn't get updated when using ObservableObject

I'm trying to build an Instagram clone app using SwiftUI.我正在尝试使用 SwiftUI 构建 Instagram 克隆应用程序。

I'm fetching the data through Firebase and trying to achieve a UI update every time the data in the server changes.我正在通过Firebase获取数据,并在每次服务器中的数据发生更改时尝试实现 UI 更新。

For some reason, when I first open the app and fetch the data, the body of my view gets called, but the UI doesn't change.出于某种原因,当我第一次打开应用程序并获取数据时,我的视图body被调用,但 UI 不会改变。 I even put a breakpoint and saw the body gets called and contains the correct information, it's just the UI which doesn't get updated.我什至设置了一个断点,看到body被调用并包含正确的信息,只是 UI 没有更新。

I have a few tabs in my app, and when I switch to another tab (which doesn't contain anything but a Text yet), suddenly the UI does gets updated.我在我的应用程序的几个标签,当我切换到另一个选项卡(不包含任何东西,但Text还),突然UI确实得到更新。

Please see the gif below:请看下面的gif:

gif

Here is my code:这是我的代码:

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

If you need me to post more code please let me know.如果您需要我发布更多代码,请告诉我。

Update:更新:

PostView :帖子视图

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

Thank you!谢谢!

The problem is that when the app starts your array is empty, and the ScrollView stops updating, you can replace it for a VStack and it will work (just for testing).问题是,当应用程序启动时,您的数组为空,并且 ScrollView 停止更新,您可以将其替换为 VStack 并且它会起作用(仅用于测试)。

The solution is to wrap the ForEach (or the ScrollView) with a condition, like this:解决方案是使用条件包装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)
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM