为什么删除孩子后我的表视图显示重复的单元格?

时间:2019-02-21 16:38:57

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

我有一个带有表视图的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中使用了此代码,并且在那儿工作正常。但是,我只是将代码从该代码复制到了此代码,并删除了一堆我不需要的东西,但是我似乎找不到在此代码中缺少的东西。

1 个答案:

答案 0 :(得分:0)

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

另一个问题是,当每个子项都被初始添加时,我们会对数组进行重新排序,然后刷新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