You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

UICollectionView拖拽后标签重复显示IndexPath问题求助

问题

我在使用两个UICollectionView,第一个包含带ID、图片的数据,第二个用于添加第一个中的单元格并支持拖拽操作。第二个UICollectionView的单元格内有一个标签,通过indexPath.item+1来显示添加数据的序号,但拖拽单元格后标签会出现序号重复的问题,请问该如何解决?

原代码

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    if collectionView == stretchAddedCV{
        return addRoutine.count
    } else {
        return self.arrRoutine.count
    }
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        if collectionView == stretchAddedCV{
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "AddedRoutineCell", for: indexPath) as! AddedRoutineCell
            let obj = self.addRoutine[indexPath.item]
            
            if let img = obj.thumbnail{
                if let imgUrl = URL.init(string: img) {
                    cell.img.kf.setImage(with: imgUrl, placeholder: PlaceHolderImage)
                }
            }
            let count = indexPath.item + 1
            cell.lblNo.text = "\(count)"
//            if addImage.indices.contains(indexPath.item) {
//                let imageUrl = addImage[indexPath.item]
//                cell.img.kf.setImage(with: URL(string: imageUrl), placeholder: PlaceHolderImage)
//            }
            return cell
        }
        else{
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "RoutineListCell", for: indexPath) as! RoutineListCell
            let obj = arrRoutine[indexPath.item]
            cell.configure(with: obj)
            if let title = obj.title {
                cell.lblTitle.text = title
            }
            if let url = obj.thumbnail {
                if let imgUrl = URL.init(string: url) {
                    cell.img.kf.setImage(with: imgUrl, placeholder: PlaceHolderImage)
                }
            }
//            cell.contentView.alpha = 1.0
//            cell.isUserInteractionEnabled = !obj.isDisable
            return cell
        }
    }

// drag and Drope Mothed
func collectionView(_ UICollectionView: UICollectionView, canMoveItemAt indexPath: IndexPath) -> Bool {
    return true
}

func collectionView(_ collectionView: UICollectionView, moveItemAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
    guard destinationIndexPath.item < addRoutine.count else {
        return
    }
    let temp = self.addRoutine.remove(at: sourceIndexPath.item)
    self.addRoutine.insert(temp, at: destinationIndexPath.item)
    
    self.stretchAddedCV.reloadItems(at: [destinationIndexPath, sourceIndexPath])       
}

//drag and Drope Longe press
@objc func handleLongGesture(gesture: UILongPressGestureRecognizer) {
    switch gesture.state {
    case .began:
        guard let selectedIndexPath = self.stretchAddedCV.indexPathForItem(at: gesture.location(in: self.stretchAddedCV)) else {
            break
        }
        self.stretchAddedCV.beginInteractiveMovementForItem(at: selectedIndexPath)
    case .changed:
        self.stretchAddedCV.updateInteractiveMovementTargetPosition(gesture.location(in: gesture.view!))
    case .ended:
        self.stretchAddedCV.endInteractiveMovement()
    default:
        self.stretchAddedCV.cancelInteractiveMovement()
    }
}
解决方案

问题根源是拖拽后你只刷新了源位置和目标位置的单元格,但所有单元格的序号都因位置变动而改变了,未刷新的单元格会保留旧序号,导致重复。

修复方法

替换拖拽方法中的刷新逻辑,有两种可选方案:

方案1:直接刷新整个列表(简单可靠)

func collectionView(_ collectionView: UICollectionView, moveItemAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
    guard destinationIndexPath.item < addRoutine.count else {
        return
    }
    let temp = self.addRoutine.remove(at: sourceIndexPath.item)
    self.addRoutine.insert(temp, at: destinationIndexPath.item)
    
    // 替换原来的reloadItems,刷新整个CollectionView
    self.stretchAddedCV.reloadData()
}

方案2:仅刷新可见单元格(性能更优)

如果列表数据量大,不想全量刷新,可以只更新当前可见的单元格:

func collectionView(_ collectionView: UICollectionView, moveItemAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
    guard destinationIndexPath.item < addRoutine.count else {
        return
    }
    let temp = self.addRoutine.remove(at: sourceIndexPath.item)
    self.addRoutine.insert(temp, at: destinationIndexPath.item)
    
    // 遍历所有可见单元格,更新序号标签
    guard let visibleCells = stretchAddedCV.visibleCells as? [AddedRoutineCell] else { return }
    for cell in visibleCells {
        if let indexPath = stretchAddedCV.indexPath(for: cell) {
            cell.lblNo.text = "\(indexPath.item + 1)"
        }
    }
}

说明

  • 方案1的reloadData()会让所有单元格重新走cellForItemAt方法,确保序号完全正确,适合数据量不大的场景。
  • 方案2只更新可见单元格,避免了不必要的刷新,适合数据量较大、追求流畅性的场景。

内容的提问来源于stack exchange,提问作者Payal Bhatt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 04:57:36