首页 > 解决方案 > Firebase - 更改/删除子项时在所有表视图中显示重复的表视图行

问题描述

我有两个问题,这两个问题都导致我的应用程序中的所有表格视图中出现重复的行:

  1. 在我的表格视图中,当我滑动以删除现有值(或行)时,一切正常,并且我的表格视图被刷新并且该行不再存在。但是,当出现新消息(或在 Firebase 中的消息下添加)时,消息表视图中的每个单元格都会重复。

  2. 此外,当当前用户更改他们的个人资料图片时,一切正常,除了当我进入消息表视图时,消息是否重复?

这是我的代码:

override func viewDidLoad() {
    super.viewDidLoad()

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

    let answerCell = UINib(nibName: "HasAnsweredYourQuestionTableViewCell", bundle: nil)
    self.table.register(answerCell, forCellReuseIdentifier: "hasAnsweredCell")




    self.questions.removeAll()

    Database.database().reference().child("messages").child(Auth.auth().currentUser!.uid)
        .observe(.childAdded) { (snap) in


            if snap.exists() {


            let obj = snap.value as! [String:Any]
            print(obj)
            let Anonymous = obj["Anonymous"] as! String
            print(Anonymous)
            let questionID = snap.key
            let mess = obj["Message"] as! String
            let timestamp = obj["timestamp"] as! Double
            let from = obj["From"] as! [String:Any]
            let username = from["username"] as? String ?? ""
            let photoURL = from["photoURL"] as? String ?? ""
            let fromID = from["uid"] as? String ?? ""

                Database.database().reference().child("profile").child(fromID).observe(.value, with: { (snapshot) in
                    let thekey = snapshot.value as? [String:Any]
                    let verifiedAsker = thekey?["isVerified"] as! String



                    self.questions.append(Question(questionID: questionID, isAnonymous: Anonymous, message: mess, timestamp: timestamp, fromID: fromID, fromPhoto: photoURL, fromUsername: username, fromVerified: verifiedAsker))
            self.questions.sort(by: {$0.createdAt > $1.createdAt})
            self.table.reloadData()

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



    }




    Database.database().reference().child("MessageIsAnswered").child(Auth.auth().currentUser!.uid).observe(.childAdded) { (snap) in
        let object = snap.key

        Database.database().reference().child("posts").child("\(object)").observe(.value) { (snapshot) in
            // Get user value
            let value = snapshot.value as! [String:Any]
            let theAnsweredQuestion = value["Answer"] as? String ?? ""
            let IdOfAnswerer = value["ToID"] as? String ?? ""
            //let photo_url = value?["photoURL"] as? String ?? ""
            let timeStampo = value["timestamp"] as! Double
            Database.database().reference().child("profile").child(IdOfAnswerer).observeSingleEvent(of: .value, with: { (snapshot) in



                let value = snapshot.value as? NSDictionary
                let photo_url = value?["photoURL"] as? String ?? ""
                let usernameOfAnswerer = value?["username"] as? String ?? ""


                self.answers.append(hasAnswered(answererID: IdOfAnswerer, answeredQuestion: theAnsweredQuestion,answererPhoto : photo_url, answererUsername: usernameOfAnswerer, timestampOfAnswer: timeStampo))



    }
}

@IBAction func navigationRightButtonTapped(_ sender: UIButton) {
    shareView.isHidden = false
}



func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    var returnValue = 0
    switch(mySegmentedControl.selectedSegmentIndex)
    {
    case 0:
        return self.questions.count
        break
    case 1:
        return self.answers.count
        break
    default:
        break
    }

    return returnValue

}




func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    let answeredCell = tableView.dequeueReusableCell(withIdentifier: "hasAnsweredCell", for: indexPath) as! HasAnsweredYourQuestionTableViewCell
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! PostTableViewCell


    switch(mySegmentedControl.selectedSegmentIndex)
    {
    case 0:



        cell.selectionStyle = .none

        cell.postTextLabel.text = questions[indexPath.row].message
        cell.subtitleLabel.text = questions[indexPath.row].createdAt.calenderTimeSinceNow()

        if self.questions[indexPath.row].fromVerified == "true"{
            cell.isVerifiedImage.image = UIImage(named: "kadabraVerifiedAccount")
        }
        else {
            cell.isVerifiedImage.image = nil

        }

        if self.questions[indexPath.row].isAnonymous != "true"{

            cell.usernameLabel.text = self.questions[indexPath.row].fromUsername
            cell.profileImageView.sd_setImage(with: URL(string:self.questions[indexPath.row].fromPhoto), placeholderImage: nil, options: .progressiveDownload, completed: nil)


        }
        else {

            cell.usernameLabel.text = ""
            cell.profileImageView.image = nil
            cell.isVerifiedImage.image = nil
        }

        break
    case 1:

    cell.selectionStyle = .none
        cell.profileImageView.sd_setImage(with: URL(string:self.answers[indexPath.row].answererPhoto), placeholderImage: nil, options: .progressiveDownload, completed: nil)
        cell.postTextLabel.text = "\(answers[indexPath.row].answererUsername) has answered your question"
        cell.subtitleLabel.text = moment(self.answers[indexPath.row].timestampOfAnswer) .fromNow()
    cell.usernameLabel.text = ""
    cell.isVerifiedImage.image = nil



        break
    default:
        break
    }

    return cell
    return answeredCell






}


@available(iOS 11.0, *)
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {


    let delete = UIContextualAction(style: .destructive, title: "Delete") { (action, view, nil) in




        Database.database().reference().child("messages").child(self.currentUserID!).child(self.questions[indexPath.row].questionID).setValue([])



        self.questions.removeAll()

        Database.database().reference().child("messages").child(Auth.auth().currentUser!.uid)
            .observe(.childAdded) { (snap) in

                if snap.exists() {



                    let obj = snap.value as! [String:Any]
                    print(obj)
                    let Anonymous = obj["Anonymous"] as! String
                    print(Anonymous)
                    let questionID = snap.key
                    let mess = obj["Message"] as! String
                    let timestamp = obj["timestamp"] as! Double
                    let from = obj["From"] as! [String:Any]
                    let username = from["username"] as? String ?? ""
                    let photoURL = from["photoURL"] as? String ?? ""
                    let fromID = from["uid"] as? String ?? ""

                    Database.database().reference().child("profile").child(fromID).observe(.value, with: { (snapshot) in
                        let thekey = snapshot.value as? [String:Any]
                        let verifiedAsker = thekey?["isVerified"] as! String



                        self.questions.append(Question(questionID: questionID, isAnonymous: Anonymous, message: mess, timestamp: timestamp, fromID: fromID, fromPhoto: photoURL, fromUsername: username, fromVerified: verifiedAsker))
                        self.questions.sort(by: {$0.createdAt > $1.createdAt})
                        self.table.reloadData()
                    })
                }
                else {
                    self.table.reloadData()

                }

        }





        }

    let report = UIContextualAction(style: .destructive, title: "Report") { (action, view, nil) in

        let areUSureAlert = UIAlertController(title: "Are you sure you want to report?", message: "This will block this user from sending you anymore questions, and also report the question to the Kadabra team.", preferredStyle: UIAlertControllerStyle.alert)



        areUSureAlert.addAction(UIAlertAction(title: "Ok", style: .default, handler: { (action: UIAlertAction!) in

            Database.database().reference().child("messages").child(self.currentUserID!).child(self.questions[indexPath.row].questionID).setValue([])

            self.questions.removeAll()

            Database.database().reference().child("messages").child(Auth.auth().currentUser!.uid)
                .observe(.childAdded) { (snap) in

                    if snap.exists() {



                        let obj = snap.value as! [String:Any]
                        print(obj)
                        let Anonymous = obj["Anonymous"] as! String
                        print(Anonymous)
                        let questionID = snap.key
                        let mess = obj["Message"] as! String
                        let timestamp = obj["timestamp"] as! Double
                        let from = obj["From"] as! [String:Any]
                        let username = from["username"] as? String ?? ""
                        let photoURL = from["photoURL"] as? String ?? ""
                        let fromID = from["uid"] as? String ?? ""
                        Database.database().reference().child("profile").child(fromID).observe(.value, with: { (snapshot) in
                            let thekey = snapshot.value as? [String:Any]
                            let verifiedAsker = thekey?["isVerified"] as! String



                            self.questions.append(Question(questionID: questionID, isAnonymous: Anonymous, message: mess, timestamp: timestamp, fromID: fromID, fromPhoto: photoURL, fromUsername: username, fromVerified: verifiedAsker))
                            self.questions.sort(by: {$0.createdAt > $1.createdAt})
                            self.table.reloadData()

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

            }



            let blockedPost = ["timestamp" : [".sv" : "timestamp"]]
            Database.database().reference().child("blocked").child(self.currentUserID!).child(self.questions[indexPath.row].fromID).setValue(blockedPost)



        }))

        areUSureAlert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { (action: UIAlertAction!) in
            //dismiss(animated: true, completion: nil)
        }))

        self.present(areUSureAlert, animated: true, completion: nil)




    }
    report.backgroundColor = #colorLiteral(red: 0.9529411793, green: 0.8918681279, blue: 0, alpha: 1)


    return UISwipeActionsConfiguration(actions: [delete, report])
}

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

    switch(mySegmentedControl.selectedSegmentIndex) {

    case 0:

    self.performSegue(withIdentifier: "answer", sender: self.questions[indexPath.row])

    case 1:

        self.performSegue(withIdentifier: "theHasAnswered", sender: self.answers[indexPath.row])


    default:
        break
    }
}

如果有人知道为什么会发生这种情况,那就太好了!

多谢你们。

标签: swiftfirebasefirebase-realtime-databasetableview

解决方案


.observe(.childAdded)方法实际上是在第一次调用时返回一个节点下的整个对象集,然后再返回新添加的对象。

此外, Firebase DB 观察者不像普通的 API 调用,您可以通过调用它来调用它。您已在代码中多次调用“消息”的观察方法。相反,只调用一次观察者。实现这一点的一个好方法是只调用didLoad一次观察者。每次您执行更改时都会触发它,例如删除条目。

还在那里执行附加逻辑并在 didSet 中为问题数组重新加载表格视图。使整个方法有点反应。

因此,重复条目的根本原因是,当您添加一个对象时,您的观察者都进入trailingSwipeActionsConfigurationForRowAtdidLoad触发,都附加到同一个数组。

还要稍微构造你的代码。一旦您开始向您的应用程序添加更多功能,它将真正帮助您。


推荐阅读