为什么删除孩子后我的表视图显示重复的单元格? [英] Why is my tableview showing duplicate cells after child is deleted?

查看:45
本文介绍了为什么删除孩子后我的表视图显示重复的单元格?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个带有表视图的ViewController,当用户单击该单元格时,它将转到VC2.当用户执行了一个操作(并更新了VC2中的值)后,我使用self.dismiss(animated: true, completion: nil)返回带有表视图的视图控制器,但是该表视图(一旦用户返回到表视图)将显示重复的行,但是在Firebase中成功删除了该子项,并创建了一个新的子项-但是,tableview显示的是未被删除两次的子项.

这是VC1中的所有相关代码:

class PostMessageListViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    @IBOutlet weak var table: UITableView!

    var topicID:namePosts?
    let currentUserID = Auth.auth().currentUser?.uid
    var posts = [Post]()

    lazy var refresher: UIRefreshControl = {

        let refreshControl = UIRefreshControl()
        refreshControl.tintColor = .white
        refreshControl.addTarget(self, action: #selector(requestData), for: .valueChanged)

        return refreshControl
    }()
    @objc
    func requestData() {
        self.table.reloadData()
        refresher.endRefreshing()
    }

    func reloadData(){

        table.reloadData()
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        self.table.separatorStyle = UITableViewCellSeparatorStyle.none

        table.refreshControl = refresher

        //DataManager.shared.firstVC = self

        self.table.delegate = self
        self.table.dataSource = self
        let postCell = UINib(nibName: "PostTableViewCell", bundle: nil)
        self.table.register(postCell, forCellReuseIdentifier: "cell")

        self.posts.removeAll()
                   Database.database().reference().child("posts").child(postID!.name)
            .observe(.childAdded) { (snap) in

                if snap.exists() {

                    //declare some values here...

                        self.posts.append( //some values here)
                        self.posts.sort(by: {$0.createdAt > $1.createdAt})
                        self.table.reloadData()

                    })
                }
                else {
                    self.table.reloadData()
                }

        }
        //observe if a post is deleted by user
        Database.database().reference().child("posts").child("posts").observe(.childRemoved) { (snapshot) in

            let postToDelete = self.indexOfPosts(snapshot: snapshot)
            self.posts.remove(at: postToDelete)
            self.table.reloadData()
            //self.table.deleteRows(at: [NSIndexPath(row: questionToDelete, section: 1) as IndexPath], with: UITableViewRowAnimation.automatic)

            //self.posts.remove(at: indexPath.row)
        }

    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.posts.count

    }

    func indexOfPosts(snapshot: DataSnapshot) -> Int {
        var index = 0
        for  post in self.posts {
            if (snapshot.key == post.postID) {
                return index
            }
            index += 1
        }
        return -1
    }

编辑:忘了说了,但是我在另一个Viewcontroller中使用了此代码,并且在那儿工作正常.但是,我只是将我的代码从该代码复制到了该代码,并删除了一堆我不需要的东西,但是我似乎找不到在此代码中缺少的东西.

解决方案

这可能不是 答案,但可能会导致 an 答案.如注释中所述,有两个数组用于管理tableView的数据源.一个包含数据,另一个使用索引技术-我相信这可能会导致问题,就像问题中描述的那样.

另一个问题是,当每个子项都被初始添加时,我们将对数组进行重新排序,然后刷新tableView-这可能会导致延迟和闪烁. (闪烁=不好)

因此,让我们建立一些基础.首先是一个拥有职位的班级

PostClass {
   var post_id = ""
   var post_text = ""
   var creation_date = ""
}

第二个Firebase结构类似

posts
   post_id_0
      text: "the first post"
      timestamp: "20190220"
   post_id_1
      text: "the second post"
      timestamp: "20190221"

然后是一些技巧,以填充数据源并保留一个添加了子级的观察者.这一点很重要,因为您不想一直刷新每个孩子的tableView,因为它可能(会)闪烁.因此,我们利用childAdded事件始终位于.value事件之前,以便填充数组,然后.value将刷新一次,然后每次之后更新tableView.这是一些代码-正在进行很多工作,因此逐步进行.

var postsArray = [String]()
var initialLoad = true

func ReadPosts() {
    let postsRef = self.ref.child("posts").queryOrdered(byChild: "timestamp")
    postsRef.observe(.childAdded, with: { snapshot in
        let aPost = PostClass()
        aPost.post_id = snapshot.key
        aPost.post_text = snapshot.childSnapshot("text").value as! String
        aPost.creation_date = snapshot.childSnapshot("timestamp").value as! String
        self.postsArray.append(aPost)

        //upon first load, don't reload the tableView until all children are loaded
        if ( self.initialLoad == false ) { 
            self.postsTableView.reloadData()
        }
    })

    //when a child is removed, the event will contain that child snapshot
    //  we locate the child node via it's key within the array and remove it
    //  then reload the tableView
    postsRef.observe(.childRemoved, with: { snapshot in
        let keyToRemove = snapshot.key
        let i = self.postsArray.index(where: { $0.post_id == keyToRemove})
        self.postsArray.remove(at: i)
        self.postsTableView.reloadData()
    })

    //this event will fire *after* all of the child nodes were loaded 
    //  in the .childAdded observer. So children are sorted, added and then
    //  the tableView is refreshed. Set initialLoad to false so the next childAdded
   //   after the initial load will refresh accordingly.
    postsRef.observeSingleEvent(of: .value, with: { snapshot in
        self.postsTableView.reloadData()
        self.initialLoad = false
    })
}

注意事项

我们让Firebase进行繁重的工作,并按creation_date排序节点,以便它们井然有序.

这可以通过viewDidLoad调用,我们首先将initialLoad类var设置为true

i have a viewcontroller with a tableview, and when user clicks on the cell, it goes to VC2. When the user has performed a action (and updated the values in VC2), i use self.dismiss(animated: true, completion: nil) to go back to the viewcontroller with the tableview, however the tableview (once the user has gone back to the tableview) is showing duplicated rows, but the child is succesfully deleted in firebase, and a new child is created - however the tableview is showing the childs that are not deleted twice.

This is all the relevant code in VC1:

class PostMessageListViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    @IBOutlet weak var table: UITableView!

    var topicID:namePosts?
    let currentUserID = Auth.auth().currentUser?.uid
    var posts = [Post]()

    lazy var refresher: UIRefreshControl = {

        let refreshControl = UIRefreshControl()
        refreshControl.tintColor = .white
        refreshControl.addTarget(self, action: #selector(requestData), for: .valueChanged)

        return refreshControl
    }()
    @objc
    func requestData() {
        self.table.reloadData()
        refresher.endRefreshing()
    }

    func reloadData(){

        table.reloadData()
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        self.table.separatorStyle = UITableViewCellSeparatorStyle.none

        table.refreshControl = refresher

        //DataManager.shared.firstVC = self

        self.table.delegate = self
        self.table.dataSource = self
        let postCell = UINib(nibName: "PostTableViewCell", bundle: nil)
        self.table.register(postCell, forCellReuseIdentifier: "cell")

        self.posts.removeAll()
                   Database.database().reference().child("posts").child(postID!.name)
            .observe(.childAdded) { (snap) in

                if snap.exists() {

                    //declare some values here...

                        self.posts.append( //some values here)
                        self.posts.sort(by: {$0.createdAt > $1.createdAt})
                        self.table.reloadData()

                    })
                }
                else {
                    self.table.reloadData()
                }

        }
        //observe if a post is deleted by user
        Database.database().reference().child("posts").child("posts").observe(.childRemoved) { (snapshot) in

            let postToDelete = self.indexOfPosts(snapshot: snapshot)
            self.posts.remove(at: postToDelete)
            self.table.reloadData()
            //self.table.deleteRows(at: [NSIndexPath(row: questionToDelete, section: 1) as IndexPath], with: UITableViewRowAnimation.automatic)

            //self.posts.remove(at: indexPath.row)
        }

    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.posts.count

    }

    func indexOfPosts(snapshot: DataSnapshot) -> Int {
        var index = 0
        for  post in self.posts {
            if (snapshot.key == post.postID) {
                return index
            }
            index += 1
        }
        return -1
    }

EDIT: Forgot to say, but i have used this code in another Viewcontroller, and it works fine there. However i just copied my code from that to this one, and deleted a bunch of stuff i didn't need, however i cant seem to find what i am missing in this one.

解决方案

This may not be the answer but it may lead to an answer. As noted in the comments there are two arrays being used to manage the dataSource for the tableView. One contains the data and one is using an indexing technique - I believe that may lead to issues, like the one described in the question.

The other issue is that when every child is intially added, we re-sort the array and then refresh the tableView - that can lead to delays and flicker. (flicker = bad)

So let establish a couple of things. First a class that holds the posts

PostClass {
   var post_id = ""
   var post_text = ""
   var creation_date = ""
}

second the Firebase structure, which is similar

posts
   post_id_0
      text: "the first post"
      timestamp: "20190220"
   post_id_1
      text: "the second post"
      timestamp: "20190221"

then a little trick to populate the datasource and leave a child added observer. This is important as you don't want to keep refreshing the tableView with every child it as may (will) flicker. So we leverage that childAdded events always come before .value events so the array will populate, and then .value will refresh it once, and then we will update the tableView each time after. Here's some code - there's a lot going on so step through it.

var postsArray = [String]()
var initialLoad = true

func ReadPosts() {
    let postsRef = self.ref.child("posts").queryOrdered(byChild: "timestamp")
    postsRef.observe(.childAdded, with: { snapshot in
        let aPost = PostClass()
        aPost.post_id = snapshot.key
        aPost.post_text = snapshot.childSnapshot("text").value as! String
        aPost.creation_date = snapshot.childSnapshot("timestamp").value as! String
        self.postsArray.append(aPost)

        //upon first load, don't reload the tableView until all children are loaded
        if ( self.initialLoad == false ) { 
            self.postsTableView.reloadData()
        }
    })

    //when a child is removed, the event will contain that child snapshot
    //  we locate the child node via it's key within the array and remove it
    //  then reload the tableView
    postsRef.observe(.childRemoved, with: { snapshot in
        let keyToRemove = snapshot.key
        let i = self.postsArray.index(where: { $0.post_id == keyToRemove})
        self.postsArray.remove(at: i)
        self.postsTableView.reloadData()
    })

    //this event will fire *after* all of the child nodes were loaded 
    //  in the .childAdded observer. So children are sorted, added and then
    //  the tableView is refreshed. Set initialLoad to false so the next childAdded
   //   after the initial load will refresh accordingly.
    postsRef.observeSingleEvent(of: .value, with: { snapshot in
        self.postsTableView.reloadData()
        self.initialLoad = false
    })
}

Things to note

We are letting Firebase doing the heavy lifting and ordering the nodes by creation_date so they come in order.

This would be called from say, viewDidLoad, where we would set the initialLoad class var to true initially

这篇关于为什么删除孩子后我的表视图显示重复的单元格?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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