I'm having 2 problems with UICollectionViews, and I somehow believe it's related to the IndexPath in both cases, however I don't really know when is the Indexpath called or what exactly it does and when it gets updated or changed.
Anyways, here are my issues:
1. Somehow the Indexpath is 0 multiple times?Youtube VIDEO showing behavior
for this particular behavior here is the code:
extension PointAllocVC: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return (playerArray?.count)!
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "playerCell", for: indexPath) as! PointAllocCell
cell.cellDelegate = self
cell.cellIndex = indexPath.row
cell.nameLabel.text = playerArray![indexPath.row].alias
cell.scoreLabel.text = String(currentScore[indexPath.row])
cell.minusBtn.layer.cornerRadius = 15
cell.plusBtn.layer.cornerRadius = 15
cell.ptsRemaining = pointsToAllocate
cell.ptsUsed = abs(pointsUsedInCells[indexPath.row])
if indexPath.row == 0 {
cell.isSelf = true
cell.layer.borderWidth = 5
cell.layer.borderColor = UIColor.blue.cgColor
} else {
cell.isSelf = false
}
cell.updatedButtons()
return cell
}
As you can see ONLY AT INDEXPATH.ROW == 0 should the cell have a border (because thats' the current user). However when I click the buttons (see button logic below) suddenly they're all index path == 0?
class PointAllocCell: UICollectionViewCell {
var isSelf: Bool = false
var cellIndex: Int?
var ptsRemaining: Int = 0
var ptsUsed: Int = 0
var cellDelegate: PointsAllocDelegate?
@IBAction func plusTapped(_ sender: Any) {
if cellDelegate != nil {
cellDelegate!.plusTapReported(fromCell: cellIndex!)
}
updatedButtons()
}
@IBAction func minusTapped(_ sender: Any) {
if cellDelegate != nil {
cellDelegate!.minusTapReported(fromCell: cellIndex!)
}
updatedButtons()
}
func updatedButtons() {
switch (ptsRemaining, ptsUsed) {
case (0,0):
plusBtn.isEnabled = false
minusBtn.isEnabled = false
case (0,_):
if isSelf{
plusBtn.isEnabled = false
minusBtn.isEnabled = true
} else {
plusBtn.isEnabled = true
minusBtn.isEnabled = false
}
case (_,0):
if isSelf{
plusBtn.isEnabled = true
minusBtn.isEnabled = false
} else {
plusBtn.isEnabled = false
minusBtn.isEnabled = true
}
default:
plusBtn.isEnabled = true
minusBtn.isEnabled = true
}
pointLabel.text = String(ptsUsed)
}
}
AND finally the delegate:
extension PointAllocVC: PointsAllocDelegate {
func plusTapReported(fromCell: Int) {
if fromCell == 0 {
//this is self
pointsToAllocate -= 1
pointsUsedInCells[fromCell] += 1
} else {
pointsToAllocate += 1
pointsUsedInCells[fromCell] += 1
}
reloadCollection()
reloadLabels()
}
func minusTapReported(fromCell: Int) {
if fromCell == 0 {
//this is self
pointsToAllocate += 1
pointsUsedInCells[fromCell] -= 1
} else {
pointsToAllocate -= 1
pointsUsedInCells[fromCell] -= 1
}
reloadCollection()
reloadLabels()
}
}
Now. The SECOND ISSUE, I'm getting it when I do the following
playerArray.remove(at: gKPlayerArray.index(of: playerDed)!)
This line of code doesn't do anything, however AFTER that when I call the "reloadData" I get that the IndexPath.row is out of range (for some reason it still thinks that the playerArray is a size X+1 even though I removed an item before calling it.
One thing has not been mentioned in answer by @rmaddy - since you are using
UICollectionView
, you shouldn’t use therow
property ofIndexPath
, but rather theitem
property. Given the nature of the collection view and its flexibility in layouting cells, the system has no way of discerning „rows”. Therow
property should be used when working withUITableView
.https://developer.apple.com/documentation/foundation/nsindexpath
A cell never needs to know its own indexPath. That's a a really bad design.
Refactor your code so you do not pass a row or indexPath to a cell.
Change your cell delegate protocol to pass the actual cell, not an
Int
.The class that actually implements the delegate methods will now be told which cell the delegate method is being called on behalf of and if needed, that class can determine the indexPath of the cell.
You also need to update your
cellForItemAt
.This code is an issue:
Whenever you set a property, you must always reset it for the other condition.
You don't need to reset the color for a 0 width border.