从 Firebase 将一组对填充到 tableView 中以仅显示前 3 个最高分数

Posted

技术标签:

【中文标题】从 Firebase 将一组对填充到 tableView 中以仅显示前 3 个最高分数【英文标题】:Populating an array of pairs into a tableView from Firebase to display only the top 3 highest scores 【发布时间】:2018-12-04 01:31:11 【问题描述】:

我正在尝试用一组对(一个 Int,一个 String)填充我的 tableView。不幸的是,我无法理解为什么它不起作用,因为我似乎无法填充 tableView,我收到 indexOutOfRange 作为错误。我使用 firebase 作为我的数据库来检索我想要填充的数据。请看下文....

class Leaderboard: UIViewController, UITableViewDataSource, UITableViewDelegate 

private let padding = 12

private var quiz: Quiz!
private let backgroundImageView = UIImageView()
private let contentView = UIView()
private let leaderboardLabel = UILabel()
private let rankLabel = UILabel()
private let userLabel = UILabel()
private let scoreLabel = UILabel()
private let tableViewCellHeight: CGFloat = 60
private let bottomButton = BottomBorderedButtons()

var scoresArray = [ScoreClass]()

var topScoresTableView: UITableView = 
    let tableView = UITableView()
    return tableView
()

override func viewDidLoad() 
    super.viewDidLoad()
    retrieveUserData()
    setupViews()


required init(quiz: Quiz) 
    super.init(nibName: nil, bundle: nil)
    defer 
        self.quiz = quiz
    


required init?(coder aDecoder: NSCoder) 
    fatalError("init(coder:) has not been implemented")
 

class ScoreClass 

var name = ""
var score = 0

init(withName: String, andScore: Int) 
    name = withName
    score = andScore
  


let ref = Database.database().reference()

func retrieveUserData() 

    let postsRef = self.ref.child("Users")
    let query = postsRef.queryOrdered(byChild: "highscore").queryLimited(toLast: 3)
    query.observeSingleEvent(of: .value, with:  snapshot in
        for child in snapshot.children 
            let snap = child as! DataSnapshot
            let dict = snap.value as! [String: Any]
            let name = dict["username"] as! String
            let score = dict["highScore"] as! Int
            let aScore = ScoreClass(withName: name, andScore: score)
            self.scoresArray.insert(aScore, at: 0)
        

        for s in self.scoresArray 
            print(s.score, s.name)
        
    )
        self.topScoresTableView.reloadData()



func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
    var countarr = 0
    for s in self.scoresArray 
        countarr = s.name.count
        print(s.score, s.name)
        print(countarr)
    
    return countarr


func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
    let cell = topScoresTableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
    let scoreClassObject = scoresArray[indexPath.row]
    let name = scoreClassObject.name
    let score = scoreClassObject.score
    cell.backgroundColor = UIColor.clear
    cell.usernameLabel.text = name
    cell.resultLabel.text = String(score)
    cell.rankNumberLabel.text = "\(indexPath.row + 1)"
    print(scoresArray)
    return cell


func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat 
    return 44


func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat 
    return 10.0


func setupViews() 
    view.addSubview(backgroundImageView)
    backgroundImageView.addSubview(contentView)
    contentView.addSubview(leaderboardLabel)
    contentView.addSubview(rankLabel)
    contentView.addSubview(userLabel)
    contentView.addSubview(scoreLabel)
    contentView.addSubview(topScoresTableView)
    contentView.addSubview(bottomButton)

    self.backgroundImageView.snp.makeConstraints  (make) in
        make.top.equalTo(topLayoutGuide.snp.bottom)
        make.leading.equalToSuperview()
        make.trailing.equalToSuperview()
        make.bottom.equalToSuperview()
    

    self.backgroundImageView.contentMode = UIViewContentMode.scaleAspectFill
    self.backgroundImageView.isUserInteractionEnabled = true
    self.backgroundImageView.image = UIImage(named: "Stars.png")

    self.contentView.snp.makeConstraints  (make) in
        make.edges.equalToSuperview()
    

    contentView.backgroundColor = transluscentGrey

    self.leaderboardLabel.snp.makeConstraints  (make) in
        make.top.equalToSuperview()
        make.leading.equalToSuperview()
        make.trailing.equalToSuperview()
        make.bottom.equalTo(topScoresTableView.snp.top).offset(-50)
        make.centerX.equalToSuperview()
    

    leaderboardLabel.textAlignment = .center
    leaderboardLabel.numberOfLines = 0
    leaderboardLabel.textColor = .white
    leaderboardLabel.font = UIFont.boldSystemFont(ofSize: 28.0)
    leaderboardLabel.text = "Leaderboard"
    leaderboardLabel.backgroundColor = tableViewCell

    self.rankLabel.snp.makeConstraints  (make) in
        make.top.equalTo(leaderboardLabel.snp.bottom).offset(10)
        make.leading.equalToSuperview().offset(padding)
        make.height.equalTo(30)
    

    rankLabel.textAlignment = .center
    rankLabel.numberOfLines = 0
    rankLabel.baselineAdjustment = .alignCenters
    rankLabel.textColor = .white
    rankLabel.font = UIFont(name: "SFUIDisplay-Medium", size: 3)

    rankLabel.layer.cornerRadius = 5.0
    rankLabel.clipsToBounds = true
    rankLabel.text = "Rank"

    self.userLabel.snp.makeConstraints  (make) in
        make.top.equalTo(leaderboardLabel.snp.bottom).offset(10)
        make.leading.equalTo(rankLabel.snp.trailing).offset(padding)
        make.height.equalTo(30)
        make.width.equalToSuperview().multipliedBy(0.50)
    

    userLabel.textAlignment = .center
    userLabel.numberOfLines = 0
    userLabel.baselineAdjustment = .alignCenters
    userLabel.textColor = .white
    userLabel.font = UIFont(name: "SFUIDisplay-Medium", size: 3)
    userLabel.layer.cornerRadius = 5.0
    userLabel.clipsToBounds = true
    userLabel.text = "Username"

    self.scoreLabel.snp.makeConstraints  (make) in
        make.top.equalTo(leaderboardLabel.snp.bottom).offset(10)
        make.leading.equalTo(userLabel.snp.trailing).offset(padding)
        make.height.equalTo(30)
        make.trailing.equalToSuperview().offset(-padding)
    

    scoreLabel.textAlignment = .center
    scoreLabel.numberOfLines = 0
    scoreLabel.baselineAdjustment = .alignCenters
    scoreLabel.textColor = .white
    scoreLabel.font = UIFont(name: "SFUIDisplay-Medium", size: 3)
    scoreLabel.layer.cornerRadius = 5.0
    scoreLabel.clipsToBounds = true
    scoreLabel.text = "Score"

    self.topScoresTableView.snp.makeConstraints  (make) in
        make.leading.equalToSuperview()
        make.trailing.equalToSuperview()
        make.height.equalToSuperview().multipliedBy(0.65)
        make.bottom.equalToSuperview().offset(-30)
    

    topScoresTableView.delegate = self
    topScoresTableView.register(TableViewCell.self, forCellReuseIdentifier: "cell")
    topScoresTableView.dataSource = self
    topScoresTableView.backgroundColor = UIColor.clear

    self.bottomButton.snp.makeConstraints  (make) in
        make.top.lessThanOrEqualTo(topScoresTableView.snp.bottom).offset(5)
        make.width.equalTo(60)
        make.bottom.equalToSuperview().offset(-5)
        make.centerX.equalToSuperview()
    
    self.bottomButton.addTarget(self, action: #selector(buttonPressed), for: .touchUpInside)
    self.bottomButton.backgroundColor = .yellow


【问题讨论】:

检查您的 numberOfRowsInSection 函数。您返回的计数错误。 即使我修改计数以返回 self.scoresArray.count。 func tableView cellForRowAt 没有被调用 self.topScoresTableView.reloadData() 放错地方了。 Firebase 是异步的,这将在闭包中读取数据之前被调用。您需要在闭包中调用 tableView.reloadData() 以便在读入数据后执行它。就在 self.scoresArray 循环中的 for 之后。 【参考方案1】:

让我们把它分解成它的部分

从 Firebase 填充并保存名称和分数的 ScoreClass 对象

class ScoreClass 
   var name = ""
   var score = 0
   init(withName: String, andScore: Int) 
      name = withName
      score = andScore
   

存放 ScoreClass 对象的数组,用作 tableView 的数据源

var scoresArray = [ScoreClass]()

读取 Firebase 并填充数组的代码

func getScoresAndNamesFromFirebaseAndStuffIntoArray() 
    let postsRef = self.ref.child("Users")
    let query = postsRef.queryOrdered(byChild: "highscore").queryLimited(toLast: 3)
    query.observeSingleEvent(of: .value, with:  snapshot in
        for child in snapshot.children 
            let snap = child as! DataSnapshot
            let dict = snap.value as! [String: Any]
            let name = dict["username"] as! String
            let score = dict["highScore"] as! Int
            let aScore = ScoreClass(withName: name, andScore: score)
            self.scoresArray.insert(aScore, at: 0)
        
        self.topScoresTableView.reloadData()
    )     

然后是 tableView 委托方法。有三个函数来处理 tableView。这假设您在 tableView 中的文本单元格的标识符为 NameScoreCell

let textCellIdentifier = "NameScoreCell"

func numberOfSections(in tableView: UITableView) -> Int 
    return 1


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


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

    let cell = tableView.dequeueReusableCell(withIdentifier: textCellIdentifier, for: indexPath as IndexPath)

    let row = indexPath.row
    let scoreObject = scoresArray[row]
    let score = scoreObject.score
    let name = scoreObject.name
    cell.nameLabel?.text = name
    cell.scoreLabel?.text = score

    return cell

【讨论】:

【参考方案2】:

替换这个函数:

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

【讨论】:

但是当我这样做时, func tableView cellForRowAt 根本不会被调用。所以 tableView 中什么都没有显示。 setupViews() 在哪里? tableView 在哪里添加到视图中? tableView 委托和数据源设置在哪里?我认为缺少一些步骤。 很抱歉没有把它们放进去,因为我认为它会有点太长了。我已将它们添加到问题中。 好的。杰伊的回答是什么时候调用 reloadData() 结合这个应该看到你是对的。

以上是关于从 Firebase 将一组对填充到 tableView 中以仅显示前 3 个最高分数的主要内容,如果未能解决你的问题,请参考以下文章

尝试将一组对象保存到 Firestore 并将其加载回来

jQuery/Javascript:如何获得一组对中的第 n 对?

如何将一组地图从 Firestore 映射到 recyclerView?

如何将一组复选框值从一个 JSP 页面传递到另一个

在PowerShell中递归地将一组文件从一个目录复制到另一个目录

我需要将一组文件从其原始位置复制到一个新文件夹