UICollection 的 IndexPath 到底是啥

Posted

技术标签:

【中文标题】UICollection 的 IndexPath 到底是啥【英文标题】:What exactly is the IndexPath of a UICollectionUICollection 的 IndexPath 到底是什么 【发布时间】:2018-01-30 16:33:01 【问题描述】:

我在使用 UICollectionViews 时遇到了 2 个问题,并且我以某种方式相信它在这两种情况下都与 IndexPath 相关,但是我真的不知道何时调用 IndexPath 或它究竟做了什么以及何时更新或更改。 无论如何,这是我的问题: 1. 不知何故Indexpath多次为0?Youtube VIDEO showing behavior

对于这种特殊行为,代码如下:

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
    

如您所见,只有在 INDEXPATH.ROW == 0 单元格应该有边框(因为那是当前用户)。但是,当我突然单击按钮(请参阅下面的按钮逻辑)时,它们都是索引路径 == 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)
    

最后是委托:

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

现在。第二个问题,当我执行以下操作时,我得到了它

playerArray.remove(at: gKPlayerArray.index(of: playerDed)!)

这行代码没有做任何事情,但是当我调用“reloadData”之后,我发现 IndexPath.row 超出范围(由于某种原因,它仍然认为 playerArray 的大小为 X+1即使我在调用它之前删除了一个项目。

【问题讨论】:

关于您的第一个问题,您还必须在 else 正文中明确设置边框,因为单元格被重复使用。 【参考方案1】:

一个单元永远不需要知道它自己的 indexPath。这是一个非常糟糕的设计。

重构您的代码,这样您就不会将行或 indexPath 传递给单元格。

更改您的单元委托协议以传递实际单元,而不是 Int

现在将告知实际实现委托方法的类代表哪个单元格调用委托方法,如果需要,该类可以确定单元格的 indexPath。

您还需要更新您的cellForItemAt

这段代码有问题:

    if indexPath.row == 0 
        cell.isSelf = true
        cell.layer.borderWidth = 5
        cell.layer.borderColor = UIColor.blue.cgColor
     else 
        cell.isSelf = false
    

每当您设置一个属性时,您必须始终为其他条件重置它。

    if indexPath.row == 0 
        cell.isSelf = true
        cell.layer.borderWidth = 5
        cell.layer.borderColor = UIColor.blue.cgColor
     else 
        cell.isSelf = false
        cell.layer.borderWidth = 0
    

您不需要重置 0 宽度边框的颜色。

【讨论】:

你能详细说明一下吗?我如何将单元格的索引发送回委托,以便它更新分数数组?就像我的函数是cellDelegate!.minusTapReported(fromCell: self) 但那会返回什么?什么类型的数据是“自我”? 您不会将索引发送给代理。这就是我的全部观点。应为代表提供单元格,而不是索引。然后,委托可以询问集合视图,该单元格的 indexPath 是什么。 好的,所以我发送“self”,然后如何将其转换为 indexPath?你有什么可以指点我的例子吗?顺便谢谢你! 查看UICollectionView 的文档。有一种明显的方法可以获取单元格的 indexPath。 func indexPath(for: UICollectionViewCell) 返回指定单元格的索引路径。然而。我如何将其传递给代表?如果我返回“self”,我会收到一堆错误,并且我无法告诉委托方法 func minusTapReported(fromCell: UICollectionViewCell) 之类的东西,因为这也会引发错误 ** EDIT ** FOUND THE ISSUE。在委托 swift 文件中缺少 Import UIKit【参考方案2】:

@rmaddy 在回答中没有提到一件事 - 因为您使用的是 UICollectionView,所以您不应该使用 IndexPathrow 属性,而应该使用 item 属性。鉴于集合视图的性质及其在布局单元格方面的灵活性,系统无法识别“行”。使用UITableView 时应使用row 属性。

var 行:整数 标识表视图部分中的行的索引号。var item: Int 标识集合视图部分中的项目的索引号。

https://developer.apple.com/documentation/foundation/nsindexpath

【讨论】:

以上是关于UICollection 的 IndexPath 到底是啥的主要内容,如果未能解决你的问题,请参考以下文章

具有动态单元标识符的 UICollectionView

iOS解决UICollectionView中使用reloadItemsAtIndexPaths进行局部cell更新导致视图重叠问题

4.UiCollection API 详细介绍

在单独的 UICollection 类中使用未解析的标识符“UICollection”

第18月第10天 iOS11 uicollectionview

ios UICollectionView 单元格选择和取消选择问题