【问题标题】:Ordered Array flickering effect?有序阵列闪烁效果?
【发布时间】:2018-11-21 04:00:30
【问题描述】:

当我从有序数组加载数据时,表格视图会在加载时遍历每个单元格。似乎表格视图先加载,然后数组排序,这会导致闪烁效果。反过来,这也会弄乱每个细胞所拥有的图像。

快照获取数据后调用

     postReference.observeSingleEvent(of: .value, with: { (snapshot) in

                if let value = snapshot.value as? NSDictionary {
                let post = Post()
                let post_content = value["post"] as? String ?? "Content not found"

                post.revealDate = value["revealedDate"] as? Double ??  0000000         
                            self.postList.append(post)
                    }

                    self.postList.sort() { $0.revealDate! > $1.revealDate! }
                    self.tableView.reloadData()

            }

这会加载单元格

public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        if queryComplete == true{


 let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! PostTableViewCell
            //set cell content
            let contentOfCellPost = postList[indexPath.row]
            cell.label?.text = contentOfCellPost.post_words
            return cell

似乎表格视图将每个单元格作为第一个单元格加载,然后对其进行排序。

编辑

整个firebase初始数据负载观察:

func fetchFeed () {
    let UID = Auth.auth().currentUser?.uid
    feedReference = Database.database().reference().child("feeds").child(UID!)
    feedReference.keepSynced(true)

    self.postRefHandle = feedReference.observe(.childAdded) { (snapshot) in
       let postID = snapshot.key
       let postReference = Database.database().reference().child("posts").child(postID)

       postReference.observeSingleEvent(of: .value, with: { (snapshot) in

           if let value = snapshot.value as? NSDictionary {
               let post = Post()
               let post_content = value["post"] as? String ?? "Content not found"
               post.Reveals = value["reveals"] as? Int ?? 10000000
               post.revealsRequired = value["revealsRequired"] as? Int ?? 100000
               post.post_words = post_content
               post.postID = postID
               post.Revealed = value["Revealed"] as? String ?? "Revealed or Not not found"
               post.revealsRequired = value["revealsRequired"] as? Int ?? 1000000000
               post.revealDate = value["revealedDate"] as? Double ??  0000000
               post.timeOfDeletion = value["timeOfDeletion"] as? Int ?? 100000000000

               if snapshot.hasChild("information") {
                   if let infoName = snapshot.childSnapshot(forPath: "information").value as? NSDictionary {
                       post.name = infoName["posterName"] as? String ?? "Poster not found"
                       post.profileImageLink = infoName["profileImage"] as? String ?? "nil"
                       post.revealedBool = true
                       self.postList.append(post)
                   }
               } else {
                  let date = Date()
                  let currentTime = date.timeIntervalSince1970 * 1000
                  let timeToDelete = Int(post.timeOfDeletion!) - Int(currentTime)
                  post.revealedBool = false
                  post.name = String(timeToDelete)
                  self.postList.append(post)
              }

              self.postList.sort() { $0.revealDate! > $1.revealDate! }
              self.tableView.reloadData()
        }
    })
  }
}

观察帖子的变化:

 postReference.observe(.value, with: { snapshot in



                if self.queryComplete == true {
                    let changedKey = snapshot.key
                    if let index = self.postList.index(where: {$0.postID == changedKey}) {
                        let indexPath = IndexPath(row: index, section: 0)
                        let changedPost = self.postList[index]
                        if let value = snapshot.value as? NSDictionary {

                            let newReveals = value["reveals"]  as? Int
                            changedPost.Reveals = newReveals
                            //update the values
                            self.tableView.reloadRows(at: [indexPath], with: UITableViewRowAnimation.none)
                        }
                        if let value = snapshot.value as? NSDictionary {
                            let newRevealStatus = value["Revealed"]  as? String ?? "noRevealStatusChange"
                            changedPost.Revealed = newRevealStatus

                            if changedPost.Revealed == "true" {
                                changedPost.revealedBool = true
                                changedPost.revealDate = value["revealedDate"] as? Double ?? 00000


                                if let newName = snapshot.childSnapshot(forPath: "information").value as? NSDictionary {

                                    changedPost.name = newName["posterName"] as? String ?? "Poster not found"
                                    changedPost.profileImageLink = newName["profileImage"] as? String ?? "nil"
                                    self.postList.sort() { $0.revealDate! > $1.revealDate! }

                                    self.tableView.reloadRows(at: [indexPath], with: UITableViewRowAnimation.none)
                                }


                            }

                        }
                    }
                }

                 self.queryComplete = true
        } )

JSON 示例:

"feeds" : {
    "3ASP4M5mkTPGPO1qQhfVKXsr6Qf2" : {
        "-L65VFpW2cYIMxG2e9Ll" : "true",
        "-L65VH5jkKPYAguzgqpn" : "true"
    }
"posts" : {
    "-L7CpeKT2lzsPALAfNti" : {
        "Revealed" : "true",
        "datePosted" : "2018-03-10 02:56:33 +0000",
        "information" : {
            "posterID" : "BmVot3XHEpYwMNtiucWSb8XPPM42",
            "posterName" : "tester",
            "profileImage" : "nil"
        },

编辑:用更少的观察者修改代码:

func fetchFeed () {

    let UID = Auth.auth().currentUser?.uid
    feedReference = Database.database().reference().child("feeds").child(UID!)
    feedReference.keepSynced(true)

    self.postRefHandle = feedReference.observe(.childAdded) { (snapshot) in
    let postID = snapshot.key
    let postReference = Database.database().reference().child("posts").child(postID)
        postReference.observe(.value, with: { (snapshot) in
            let postKey = snapshot.key
             if let value = snapshot.value as? NSDictionary {
            if let index = self.postList.index(where: {$0.postID == postKey}) {
                let indexPath = IndexPath(row: index, section: 0)
                let changedPost = self.postList[index]


                    let newReveals = value["reveals"]  as? Int
                    changedPost.Reveals = newReveals
                    //update the values
                    self.tableView.reloadRows(at: [indexPath], with: UITableViewRowAnimation.none)

                    let newRevealStatus = value["Revealed"]  as? String ?? "noRevealStatusChange"
                    changedPost.Revealed = newRevealStatus

                    if changedPost.Revealed == "true" {
                        changedPost.revealedBool = true
                        changedPost.revealDate = value["revealedDate"] as? Double ?? 00000


                        if let newName = snapshot.childSnapshot(forPath: "information").value as? NSDictionary {

                            changedPost.name = newName["posterName"] as? String ?? "Poster not found"
                            changedPost.profileImageLink = newName["profileImage"] as? String ?? "nil"
                            self.postList.sort() { $0.revealDate! > $1.revealDate! }


                            self.roundButton.isHidden = false
                        }


                    }

                  self.tableView.reloadRows(at: [indexPath], with: UITableViewRowAnimation.none)
            }

                //appending to row for first time (initial load)
            else {
                if let value = snapshot.value as? NSDictionary {
                    let post = Post()
                    let post_content = value["post"] as? String ?? "Content not found"
                    post.Reveals = value["reveals"] as? Int ?? 10000000
                    post.revealsRequired = value["revealsRequired"] as? Int ?? 100000
                    post.post_words = post_content
                    post.postID = postID
                    post.Revealed = value["Revealed"] as? String ?? "Revealed or Not not found"
                    post.revealsRequired = value["revealsRequired"] as? Int ?? 1000000000
                    post.revealDate = value["revealedDate"] as? Double ??  0000000
                    post.timeOfDeletion = value["timeOfDeletion"] as? Int ?? 100000000000

                    if snapshot.hasChild("information"){

                        if let infoName = snapshot.childSnapshot(forPath: "information").value as? NSDictionary {

                            post.name = infoName["posterName"] as? String ?? "Poster not found"
                            post.profileImageLink = infoName["profileImage"] as? String ?? "nil"
                            post.revealedBool = true
                            self.postList.append(post)


                        }
                    }
                    else  {
                        let date = Date()
                        let currentTime = date.timeIntervalSince1970 * 1000
                        let timeToDelete = Int(post.timeOfDeletion!) - Int(currentTime)
                        post.revealedBool = false
                        post.name = String(timeToDelete)

                        self.postList.append(post)

                    }
                    // self.deletePosts()

                    self.postList.sort() { $0.revealDate! > $1.revealDate! }

                    self.tableView.reloadData()



                }

            }
            }
             else {
               Database.database().reference().child("feeds").child(self.userIDCurrent!).child(postID).removeValue()
            }
        })
    }
    self.queryComplete = true
}

【问题讨论】:

  • @Code 不同,如何在添加单元格之前重新加载特定行?如何获取索引路径?
  • 通过 .value 观察节点的代码看起来有点奇怪。您能否在问题中包含您的 Firebase 结构的 sn-p 作为文本,以便我们查看?
  • @Jay,我更新了代码。 json 结构使用扇出系统,以便帖子 ID 位于每个提要中。在观察数据时,我最初收集提要中的每个帖子,然后为初始加载创建一个单独的引用。然后,我为每篇只观察变化的帖子做一个参考,但为简洁起见,我没有包含代码。
  • 事件的顺序有点不清楚,但我突然想到的一件事是(如果我误解了,请纠正我)你正在使用 postReference.observeSingleEvent填充 dataSource 数组,但在某些时候会发生这种情况 postReference.observe(.value 本质上将重新加载相同的数据但留下一个观察者。第一个观察更新整个 tableView,第二个更新那些相同的行再次。似乎所有这些都可以组合成一个观察者调用来填充帖子数组并让观察者观察这些帖子的变化。

标签: swift uitableview firebase firebase-realtime-database tableview


【解决方案1】:

我将对此进行尝试,并通过建议替代方向来解决此问题。如果这太离谱了,请告诉我,我可以更新或删除。

问题中有很多代码需要整理,但从表面上看,似乎添加了很多观察者,重新加载数据并频繁刷新 tableView。

据我所知,feeds 节点被用来收集数据组;用户感兴趣的一组帖子、一组日期、一组朋友等。每一个都是一个提要,连接到该提要的每个节点都添加了一个观察者。

我的建议是在更高的层次上进行 - 而不是用一个单独的、独立的观察者来处理每个子节点,让我们说'嘿 Firebase,在这个父节点中,如果一个子节点满足某个标准,让用户知道它'

因此,让我们利用 Firebase 深度查询以更少的观察者来做同样的事情。

以帖子结构为例

posts
   post_0
     name: "Jim"
     post: "We have them just where they want us"
     watched_by:
        uid_0: true
        uid_1: true
   post_1
     name: "Spock"
     post: "Nothing unreal exists"
     watched_by:
        uid_1: true
   post_2
     name: "Bones"
     post: "I'm a doctor, not an escalator"
     watched_by
        uid_0: true

并假设具有 uid_0 的用户进行了身份验证。此前,他们决定观看 Jim and Bones 的帖子。从结构中可以看出,我们在每个帖子中都有一个 watch_by 节点,并在该节点中添加了 uid_0: true 。

所以,现在让我们让 uid_0 观察帖子节点以了解他们感兴趣的帖子的变化:

let postsRef = self.ref.child("posts")
let watchedByQuery = postsRef.queryOrdered(byChild: "/watched_by/uid_0").queryEqual(toValue: true)
watchedByQuery.observe(.childChanged, with: { snapshot in
    let dict = snapshot.value as! [String: Any]
    let post = dict["post"] as! String
    print(snapshot.key, post)
})

因此,如果 Bones 登录并将他的帖子从“我是医生,不是自动扶梯”更改为“我是医生,不是瓦工”,该节点将被发送到 uid_0,以便可以更新 ui。

   post_2
     name: "Bones"
     post: "I'm a doctor, not a bricklayer"
     watched_by
        uid_0: true

对此进行扩展,我们显然会使用相同的技术添加 .childAdded 和 .childRemoved,并且 .childAdded 会在每个用户进行身份验证时最初为他们填充我们的 tableView 数据源。

假设 Bones 然后完全删除了他的帖子(节点)。 watch_by 列表中的任何用户都会收到该事件的通知(甚至是数百万用户)。发生这种情况时,在代码中,从快照中读取 post_2 的键,在您的数据源数组中找到它,删除它并更新您的 tableView。

再次 - 对我来说,这是一个完全黑暗的答案。

【讨论】:

  • 我有点进退两难。您提供的结构更加高效,减少了观察者,并防止了闪烁。但是,该帖子会将所有 watch_by 数据发送给正在访问它的每个用户,并且整个想法应该是关于匿名发布。因此,当用户访问帖子时,虽然 UI 不显示它,但所有数据(包括用户的朋友是谁)都会发送到每个提要……这是一个严重的问题吗?关于如何避免这种情况的任何建议?
  • @RaimKhalil UID 非常通用,不包含任何直接用户信息。因此,即使应用程序收到了一堆 uid,也没有回溯来查看那是谁。更重要的是,用户必须调试您的源代码,然后检查该快照以首先查看 uid 是什么。如果它让您更舒服,请不要使用 UID,使用其他随机生成的字符串或 UUID 并将其存储在每个用户/用户节点中,例如 /users/uid_x/my_random_code: xyzzy 然后使用该代码代替要观察的uid。
  • @RaimKhalil 这是来自 Firebaser 的回答的链接,它与 UID 安全UID
  • 这很有意义!感谢过去几天的所有帮助,我认为 UID 很好,那么,就安全性而言。
  • @RaimKhalil 查询在服务器端完成,发送到您的应用的唯一数据是与查询匹配的数据。换句话说,如果您将 childAdded 查询添加到 /watched_by/uid_0 的节点,则将馈送到您的应用程序的唯一节点是添加时匹配的节点。其他的被忽略。
猜你喜欢
  • 2012-04-05
  • 1970-01-01
  • 2011-09-07
  • 1970-01-01
  • 2019-01-05
  • 2011-09-06
  • 2013-06-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多