dvd.Void
dvd.Void

Reputation: 369

UITableView: IndexPath.row out of range (FATAL ERROR)

I have a tableview that lists the people connected to a game. When a player is eliminated, I run the following code:

func doRemovePlayer(playerDed: GKPlayer) {

    print("\n ** Removing \(playerDed.alias!) from the game! ** \n")
    gameLog += "Removing \(playerDed.alias!) from the game!"
    let elIndex = (gKPlayerArray.index(of: playerDed))!
    scoreArray.remove(at: elIndex)
    randomNoArray.remove(at: elIndex)
    timeResultsArray.remove(at: elIndex)
    answerResultArray.remove(at: elIndex)
    playersReadyForNextStage.remove(at: elIndex)
    gKPlayerArray.remove(at: elIndex)
    noOfPlayers -= 1
    print ("New Array counts are: \(scoreArray.count), \(randomNoArray.count), \(timeResultsArray.count), \(answerResultArray.count), \(playersReadyForNextStage.count), \(gKPlayerArray.count), Players: \(noOfPlayers)")
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.01, execute: {
        self.scoreBoardTable.reloadData()
    })
    checkIfWinnerOrLoser()
}

As you can see I do a dispatch queue just to avoid any problems (I think). Anyways, as I run this code on 2 simulators and my actual iPhone, the simulators don't have an issue with this code, however the iPhone 3/4 times gives me a FATAL ERROR: INDEX OUT OF RANGE and points to this particular line in the code:

extension GameScreenVC: UITableViewDelegate, UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return gKPlayerArray.count
    }

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let scoreCell = tableView.dequeueReusableCell(withIdentifier: "playerCell") as! ScoreBoardTableCell
    **scoreCell.nameLabel.text = gKPlayerArray[indexPath.row].alias!** //FATAL ERROR: INDEX OUT OF RANGE
    scoreCell.scoreLabel.text = String(scoreArray[indexPath.row])
    if indexPath.row == 0 {
        scoreCell.layer.borderWidth = 3
        scoreCell.layer.borderColor = UIColor.blue.cgColor
    } else {
        scoreCell.layer.borderWidth = 0
    }
    scoreCell.statusLabel.layer.cornerRadius = 7
    if playersReadyForNextStage[indexPath.row] {
        scoreCell.statusLabel.layer.backgroundColor = UIColor.yellow.cgColor
    } else {
        scoreCell.statusLabel.layer.backgroundColor = UIColor.green.cgColor
    }
    return scoreCell
}

I am not sure exactly how to tackle this problem. I do "Print" the new values of all the arrays just in case and I do confirm that they are all correct (they were 3 now they are 2). Then when it runs the update for the table it crashes thinking there's a 3rd element in indexPath.row? (I think that's whats happening). Is IndexPath.row like not updated or something? is there a way to update it more properly?

Upvotes: 0

Views: 433

Answers (1)

Milan Nosáľ
Milan Nosáľ

Reputation: 19757

If doRemovePlayer is called on main thread, you have no reason to dispatch reload later. Try simply this:

func doRemovePlayer(playerDed: GKPlayer) {

    print("\n ** Removing \(playerDed.alias!) from the game! ** \n")
    gameLog += "Removing \(playerDed.alias!) from the game!"
    let elIndex = (gKPlayerArray.index(of: playerDed))!
    scoreArray.remove(at: elIndex)
    randomNoArray.remove(at: elIndex)
    timeResultsArray.remove(at: elIndex)
    answerResultArray.remove(at: elIndex)
    playersReadyForNextStage.remove(at: elIndex)
    gKPlayerArray.remove(at: elIndex)
    noOfPlayers -= 1
    print ("New Array counts are: \(scoreArray.count), \(randomNoArray.count), \(timeResultsArray.count), \(answerResultArray.count), \(playersReadyForNextStage.count), \(gKPlayerArray.count), Players: \(noOfPlayers)")
    // it should be enough to delete the corresponding row
    scoreBoardTable.deleteRows(at: [IndexPath(row: elIndex, section: 0)], with: .automatic)
    checkIfWinnerOrLoser()
}

Upvotes: 1

Related Questions