添加新值后,Firebase数据闪烁

时间:2018-11-07 01:53:24

标签: swift firebase firebase-realtime-database uicollectionview

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

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

users
  |- <user_id>
         |- name

以下是我在collectionVC中获取发布数据并将所有数据存储到posts数组的方式:

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

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

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

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

1 个答案:

答案 0 :(得分:1)

您现在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: )才能将该项放置在列表中的正确位置。