使用ObservableObject时视图不会更新 [英] View doesn't get updated when using ObservableObject

查看:29
本文介绍了使用ObservableObject时视图不会更新的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用SwiftUI构建Instagram克隆应用.

I'm trying to build an Instagram clone app using SwiftUI.

我正在通过 Firebase 获取数据,并试图在每次服务器中的数据更改时实现UI更新.

I'm fetching the data through Firebase and trying to achieve a UI update every time the data in the server changes.

由于某种原因,当我第一次打开应用程序并获取数据时,会调用我视图的 body ,但是UI不变.我什至放了一个断点,看到 body 被调用并包含正确的信息,只是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. 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.

我的应用程序中有几个选项卡,当我切换到另一个选项卡(除了 Text 之外不包含任何内容)时,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.

请参见下面的gif:

这是我的代码:

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.

更新:

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

发布:

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并且可以正常工作(仅适用于测试).

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

解决方案是使用以下条件包装 ForEach (或ScrollView):

The solution is to wrap the ForEach(or the ScrollView) with a condition, like this:

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

这篇关于使用ObservableObject时视图不会更新的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆