拖放 UICollectionView 单元格重用问题

Posted

技术标签:

【中文标题】拖放 UICollectionView 单元格重用问题【英文标题】:Drag & Drop UICollectionView cell reuse problem 【发布时间】:2020-06-14 04:17:01 【问题描述】:

所以我已经为我的 UICollectionView 实现了拖放,如下所示:

func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] 
        guard shouldAllowDragForIndexPath?(indexPath) == true else  return [] 
        guard let cell = collectionView.cellForItem(at: indexPath)?.toImage() else 
            return []
        
        let provider = NSItemProvider(object: cell)
        let item = UIDragItem(itemProvider: provider)
        item.localObject = data[indexPath.row]

        return [item]
    

    func collectionView(_ collectionView: UICollectionView, performDropWith coordinator: UICollectionViewDropCoordinator) 
        guard
            let item = coordinator.items.first?.dragItem,
            let sourceIndexPath = coordinator.items.first?.sourceIndexPath,
            let destinationIndexPath = coordinator.destinationIndexPath,
            let entity = item.localObject as? T
            else 
                return
        
        collectionView.performBatchUpdates( [unowned self] in
            dropEntityAtIndexPath?(entity, destinationIndexPath)
            self.collectionView.deleteItems(at: [sourceIndexPath])
            self.collectionView.insertItems(at: [destinationIndexPath])
        , completion: nil)
        coordinator.drop(item, toItemAt: destinationIndexPath)
        item.localObject = nil
    

    func collectionView(_ collectionView: UICollectionView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UICollectionViewDropProposal 
        if
            session.localDragSession != nil,
            let path = destinationIndexPath,
            shouldAllowDropForIndexPath?(path) == true 
            return UICollectionViewDropProposal(operation: .move, intent: .insertAtDestinationIndexPath)
         else 
            return UICollectionViewDropProposal(operation: .forbidden)
        
    

一切正常,但我发现重复使用单元格时出现了奇怪的行为。 这是视频:link

所以它只发生在拖动单元格之后,而且似乎有一些灰色的覆盖视图,但我在视图调试器上看不到它。另外,我尝试将单元格的imageView.image = nil 设置为prepareForReuse,但没有帮助。如果有人帮我解决这个问题,我将不胜感激。谢谢。

Edit1:cellForItemAt中的代码:

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
    let cell = collectionView.dequeueReusableCell(for: indexPath, cellType: PhotoCollectionViewCell.self)
        let container = data[indexPath.row].postContainer,
        let post = container.postMedia.first
        let isCarousel = container.isCarousel
        let isVideo = post.isVideo
        let _isSelected = isSelected?(indexPath) ?? false
        let url = post.thumbURL?.toURL()
        let isScheduled = container.schedulerFor != nil
        let fromInstagram = false

        cell.configure(url: url, isSelected: _isSelected, isVideo: isVideo, isCarousel: isCarousel, isScheduled: isScheduled, fromInstagram: fromInstagram)

    return cell

编辑 2 单元配置函数:

func configure(url: URL?, isSelected: Bool = false, isVideo: Bool = false, isCarousel: Bool = false, isScheduled: Bool = false, fromInstagram: Bool = false) 
        checkmarkImageView.isHidden = !isSelected
        selectionOverlay.isHidden = !isSelected
        isVideoImageView.isHidden = !isVideo
        carouselImageView.isHidden = !isCarousel
        cornerImageView.image = isScheduled ? R.image.cyanCorner() : R.image.grayCorner()
        instagramImageView.isHidden = !fromInstagram
        cornerImageView.isHidden = fromInstagram
        url.map(imageView.setImage(with:))
    

【问题讨论】:

你的cellForItem(at:)函数是什么样的? @JacobRelkin 已添加到问题中。 ...你的configure 函数是什么样的? @JacobRelkin 已添加到问题中。 @JacobRelkin 如果之前发生拖放,则始终在滚动之后。 【参考方案1】:

我认为问题不是细胞重用,而是模型更新不当。 item.localData 应该在移动完成后正确更新。

这意味着你必须在调用UICollectionView更新之前更新你的数据模型对象。

这是UICollectionViewDelegate 的示例,但实际上只有这行很重要:

// YOUR MODEL OBJECT is expected to be a list-like data structure
YOUR_MODEL_OBJECT.insert(YOUR_MODEL_OBJECT.remove(at: sourceIndex), at: destinationIndex)

UICollectionViewDelegate 的工作原理

func collectionView(_ collectionView: UICollectionView, moveItemAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) 
    guard destinationIndexPath != sourceIndexPath else  return 
    // YOUR MODEL OBJECT is expected to be a list-like data structure
    YOUR_MODEL_OBJECT.insert(YOUR_MODEL_OBJECT.remove(at: sourceIndex), at: destinationIndex)

【讨论】:

感谢您的回答,我已经尝试过您的解决方案,但对我没有帮助:(

以上是关于拖放 UICollectionView 单元格重用问题的主要内容,如果未能解决你的问题,请参考以下文章

如何在 UICollectionView 拖放期间删除“幽灵”单元格,并使移动单元格不透明?

UICollectionView 不重用单元格

重用单元格 UICollectionView - 某些单元格没有被选中

UICollectionView 不重用单元格

UICollectionView 重用单元格问题

在 UICollectionView 上禁用单元格重用