首页 > 解决方案 > Firebase Data 在添加新值时闪烁

问题描述

我正在制作一个社交应用程序,我正在获取一些数据并将其刷新到集合视图。我正在将所有帖子从 firebase 刷新到帖子数组。我还在获取发布特定图像的用户信息。两个数据库都是 2 个不同的模型。以下是我的数据模型:

posts
  |- <post_id>
         |- caption
         |- ImageURL
         |- views
         |- spot
             |- spot_id
                  |- sender<user_id>
                  |- spotted(value)
                  |- timestamp
         |- author(<user_id>)

users
  |- <user_id>
         |- name

以下是我在 collectionVC 中获取帖子数据并将所有内容存储到帖子数组的方式:

func initialiseAllPostsContent(){
    FBDataservice.ds.REF_CURR_USER.child("connections/following").observe(.childAdded) { (snapshot) in
        if let snapshot = snapshot.value as? String {
            self.followerKeys.append(snapshot)
        }
    }
    if uid != nil {
        self.followerKeys.append(uid!)
    }
    FBDataservice.ds.REF_POSTS.queryOrdered(byChild: "timestamp").observe(.childAdded, with: { (snapshot) in
        print("post key is ", snapshot.key)
        if let postDict = snapshot.value as? Dictionary<String, Any> {
            let key = snapshot.key
            if let postAuthor = postDict["author"] as? String {
                for user in self.followerKeys {
                    if postAuthor == user {
                        let post = Posts(postId: key, postData: postDict)
                        self.posts.append(post)
                    }
                }
            }
        }
    })
    reloadCollectionViewData()
}

func reloadCollectionViewData() {
    FBDataservice.ds.REF_POSTS.queryOrdered(byChild: "timestamp").observe(.value) { (snapshot) in
        self.collectionView.reloadData()
    }
}

//I am updating the views on the post after a method is successfull. As soon as this is called, and then if like is pressed, views flicker
func updateViews(postid: String, views: Int) {
    let viewref = FBDataservice.ds.REF_POSTS.child(postid)
    let newviews = views + 1
    viewref.updateChildValues(["views":newviews])
}

// fetching the user data from the post data

func getAllPosts(pid: String, completion: @escaping ((String) -> ())) {
    FBDataservice.ds.REF_POSTS.child(pid).observeSingleEvent(of: .value) { (snapshot) in
        if let snapshot = snapshot.value as? Dictionary<String, Any> {
            if let userid = snapshot["author"] as? String {
                completion(userid)
            }
        }
    }
}

func getpostAuthorData(authorId : String, completion: @escaping (User) -> ()) {
    FBDataservice.ds.REF_USERS.child(authorId).observeSingleEvent(of: .value) { (snapshot) in
        if let snapshot = snapshot.value as? Dictionary<String, Any> {
            if let userCredential = snapshot["credentials"] as? Dictionary<String, Any> {
                completion(User(userid: authorId, userData: userCredential))
            }
        }
    }
}

这就是我在我的cellForItemAtIndexPath

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    self.posts.sort(by: { $0.timestamp < $1.timestamp})
    let post = posts[indexPath.row]
    if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as? SpotGroundCell {
        cell.configureCellData(post: post)
        getAllPosts(pid: post.postId) { (userid) in
            self.getpostAuthorData(authorId: userid, completion: { (userdata) in
                cell.configUserData(user: userdata)
            })
        }
    return cell
    } else {
        return SpotGroundCell()
    }

}

我单元格中的代码:

//Consider this as likes. I allow users to like multiple times. Once the model is loaded, it fetches all the spots according to the timestamp and then siplayer the most recent ones. Even this is doesn't display according to the current image and flickers. I replicate previous cell values even though I am refreshing the view.
var currentUserSpots = [Spot]() {
    didSet {
        self.currentUserSpots.sort(by: { $0.timestamp < $1.timestamp})
        if !self.currentUserSpots.isEmpty {
            self.emotionImage.image = UIImage(named: (self.currentUserSpots.first?.spotted)!)
            self.emotionImage.alpha = 1
        } else {
            self.emotionImage.image = UIImage(named: "none")
            self.emotionImage.alpha = 0.5
        }
    }
}

func configUserData(user: User) {
    self.user = user
    self.name.text = self.user.name
}

func configureCellData(post: Posts) {
    print("Config is now called")
    self.posts = post
    self.caption.text = posts.caption

    FBDataservice.ds.REF_POSTS.child(post.postId).child("spot").queryOrdered(byChild: "senderID").queryEqual(toValue: uid!).observeSingleEvent(of: .childAdded) { (snapshot) in
        if let spotData = snapshot.value as? Dictionary<String, Any> {
            let spot = Spot(id: snapshot.key, spotData: spotData)
            if spot.spotted != nil {
                self.currentUserSpots.append(spot)
            }
        }
    }
}

现在,每当我进行更改或更新数据库的事件(例如更新视图)时。我在用户对象实体(例如名称等)中看到闪烁。该事件还会杀死其他进程和通知观察者。

我放弃了互联网的解决方案,但到目前为止只能找到一个,这并不能解决我的问题。

任何帮助将不胜感激。我真的不确定我哪里出错了。

标签: swiftfirebasefirebase-realtime-databaseuicollectionview

解决方案


REF_POSTS每当您现在有变化时:

  1. 从视图中删除所有数据
  2. 将所有数据(包括更改)重新添加到视图中

鉴于大多数更改只会影响列表中的一项,因此您对 N-1 的查看超出了需要。这会导致闪烁。

要解决这个问题,您应该从数据库中收听更细粒度的信息。而不是观察.value,添加一个监听器.childAdded。每当添加新的孩子时,都会触发此侦听器的完成块,此时您可以将新孩子添加到您的视图中。

FBDataservice.ds.REF_POSTS.queryOrdered(byChild: "timestamp").observe(.childAdded, with: { (snap) in
    if let postDict = snap.value as? Dictionary<String, Any> {
        let key = snap.key
        if let postAuthor = postDict["author"] as? String {
            for user in self.followerKeys {
                if postAuthor == user {
                    let post = Posts(postId: key, postData: postDict)
                    self.posts.append(post)
                }
            }
        }
    }
})

作为奖励.childAdded,所有现有子节点也会立即触发,因此您不再需要观察者.value。不过我喜欢自己留着。由于 Firebase 保证它会.value在所有相应child*事件之后触发,因此该.value事件是告诉视图所有更改都已发生的好时机。

FBDataservice.ds.REF_POSTS.queryOrdered(byChild: "timestamp").observe(.value, with: { (snapshot) in
    self.collectionView.reloadData()
})

你需要更多的东西来完成一个完整的实现:

  1. 您还应该观察和.childChanged处理对数据库的那些类型的更改。.childMovedchildRemoved
  2. 由于可以在列表中的任何位置添加(或移动)子项,因此您实际上应该使用observe(_, andPreviousSiblingKey: )能够将项目放在列表中的正确位置。

推荐阅读