【问题标题】:Firebase - Showing duplicate tableview rows in all tableviews when child is changed/deletedFirebase - 更改/删除子项时在所有表视图中显示重复的表视图行
【发布时间】:2018-10-03 17:25:39
【问题描述】:

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

  1. 在我的 tableview 中,当我滑动删除现有值(或行)时,一切正常,并且我的 tableview 被刷新并且该行不再存在。但是当出现新消息(或在 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
    }
}

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

谢谢大家。

【问题讨论】:

  • 只是一个注释(因为我没有看过所有代码),当在一个孩子中设置多个值时,最好将字典设置为master(父母的父母......等)那个物体。否则,如果您对 每个 孩子执行 setValue(),这将导致多次连续更新。

标签: swift firebase firebase-realtime-database tableview


【解决方案1】:

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

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

还在那里执行附加逻辑并在 didSet 中为 questions 数组重新加载 tableview。使整个方法有点被动。

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

还可以稍微组织一下您的代码。一旦您开始为您的应用添加更多功能,它将真正帮助您。

【讨论】:

  • 但是我要添加什么来使它在删除时单元格消失?如果我只做 .setValue[ ] ,然后 self.table.reloadData (在 trailingSwipe 上) - 会这样做吗?并且只是在 viewdidload 中有一个观察者?感谢您的回答
  • 当你调用 setValue 时,.childChanged 观察者会在你调用 setValue 后自动触发。然后,您可以修改数据源数组,然后调用 reload。一个好主意是添加一个通用的 listenForAllEvents 观察者,它返回 evenType(可能是 .childAdded、.childMoved、.childRemoved、.childChanged),并在数组中执行更改。然后调用 reload
  • 查看这个要点gist.github.com/ravitripathi/3efac64eea18e5d701c7472704f93361。只需将其添加到您的 didLoad 中即可。在它的完成块中,使用 DataEventType 来确定发生了什么变化,并相应地更新你的数组
  • 好的,但是我应该在 viewdidload 中添加 .childAdded、.childMoved、.childRemoved、.childChanged 吗?然后将所有这些附加到同一个数组中?
  • 是的。只需相应地执行更改。所以对于 childAdded 追加到数组,对于 childRemoved 从数组中删除它。如果您不关心顺序,则可以忽略 childMoved (我假设您不关心,因为您对数组进行了排序)。对于 childChanged,您必须更新特定行。同样,您可以忽略这一点,因为您目前没有处理现有数据。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多