移动时附加到 UICollectionView 中的单元格的弹出框被称为此集合视图的 ReloadData

Posted

技术标签:

【中文标题】移动时附加到 UICollectionView 中的单元格的弹出框被称为此集合视图的 ReloadData【英文标题】:Popover attached to cell in UICollectionView moving when is called the ReloadData of this collection view 【发布时间】:2020-02-14 13:40:02 【问题描述】:

我有一个用户可以单击的 UICollectionView,它显示了一个附加到单击的单元格的弹出框。在 ios 12 中,无论是否调用了重新加载数据,弹出框都保持在同一原点(x、y 或单元格),但在 iOS 13 中,当调用集合视图的重新加载数据时,弹出框在单元格之间移动。

关注 iOS 13 中的行为视频: https://vimeo.com/385021234

演示是使用以下代码完成的:

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) 
    guard let cell = collectionView.cellForItem(at: indexPath) else 
        return
    

    presentPopover(from: self, cell: cell)


func presentPopover(from view: UIViewController, cell: UIView) 
    let popoverView = PopoverViewController(nibName: "PopoverViewController", bundle: nil)
    let popover: UIPopoverPresentationController = popoverView.popoverPresentationController!

    popover.sourceRect = cell.bounds
    popover.sourceView = cell

    view.present(popoverView, animated: true, completion: nil)

而 PopoverViewController 正在使用 modalPresentationStyle = .popover

之前有人在 iOS 13 中遇到过这个问题吗? 我们的应用在 iOS 11 和 12 中运行良好。

我在以下 git 存储库中有此行为的示例:https://github.com/diegodossantos95/UICollectionViewPopoverBug

在存储库中重现的步骤:

    点击收集单元格

    弹出窗口打开

    等待重新加载数据

谢谢,

迭戈。

【问题讨论】:

我现在找到了一种解决方法:当弹出框可见时手动更新每个可见单元格,因此我不需要调用 reloadItems 和 reloadData。 【参考方案1】:

您可以使用从单元格的框架创建的临时 UIView,然后将弹出框附加到该单元格,而不是将弹出框附加到您可以保证不会出队的单元格。这是完成此操作的相关代码。

class ViewController: UIViewController 
    var timer = Timer()

    var popOverTempView = UIView()

    @IBOutlet weak var collectionView: UICollectionView!

    override func viewDidLoad() 
        super.viewDidLoad()
        timer = Timer.scheduledTimer(timeInterval: 10, target: self, selector: #selector(reloadData), userInfo: nil, repeats: true)
        self.collectionView.addSubview(popOverTempView)
        popOverTempView.isHidden = true
        popOverTempView.backgroundColor = .clear
    



extension ViewController: UICollectionViewDelegate 

    func presentPopover(from view: UIViewController, cell: UIView) 
        let popoverView = PopoverViewController(nibName: "PopoverViewController", bundle: nil)
        let popover: UIPopoverPresentationController = popoverView.popoverPresentationController!

        popoverView.delegate = self

        popOverTempView.frame = cell.frame
        popOverTempView.isHidden = false
        popover.sourceRect = popOverTempView.bounds
        popover.sourceView = popOverTempView

        view.present(popoverView, animated: true, completion: nil)

    



extension ViewController: PopoverViewControllerDelegate 
    func willDismissPopover() 
        popOverTempView.isHidden = true
    


protocol PopoverViewControllerDelegate 
    func willDismissPopover()


class PopoverViewController: UIViewController 

    var delegate: PopoverViewControllerDelegate?

    override func viewWillDisappear(_ animated: Bool) 
        super.viewWillDisappear(animated)
        delegate?.willDismissPopover()
    

2020 年 5 月 20 日更新:

我想我找到了一个更干净更直接的解决方法。与其直接使用单元格的边界,不如将其 contentView 的框架转换为 collectionView 的坐标空间,然后将其用作 sourceRect 并从当前视图控制器的视图中呈现。

func presentPopover(from view: UIViewController, cell: UICollectionViewCell, indexPath: IndexPath) 
    let popoverView = PopoverViewController(nibName: "PopoverViewController", bundle: nil)
    let popover: UIPopoverPresentationController = popoverView.popoverPresentationController!

    var rect = cell.convert(cell.contentView.frame, to: collectionView)
    rect = collectionView.convert(rect, to: self.view)

    popover.sourceRect = rect
    popover.sourceView = self.view

    view.present(popoverView, animated: true, completion: nil)

【讨论】:

谢谢,您的解决方法有效,但我仍在寻找更干净的方法来修复它。我不明白为什么我的代码在 iOS 11/12 中运行良好,而不是在 13 中。 我能找到的最佳解决方法是从集合中获取所有可见单元格,然后在弹出框可见时手动更新每个单元格。 我想我可能已经找到了您最初正在寻找的修复程序。检查答案的更新部分。 太棒了,我要测试一下。谢谢。【参考方案2】:

当您调用 reloadData 时,这会导致集合视图丢弃所有当前可见的项目(包括占位符)并根据数据源对象的当前状态重新创建项目。

查看reload数据前后cell帧的差异:

  - some : <UICollectionViewCell: 0x7fd8abd02190; frame = (73.5 10; 50 50); clipsToBounds = YES; hidden = YES; opaque = NO; layer = <CALayer: 0x600001b50e20>>
  - some : <UICollectionViewCell: 0x7fd8abd02190; frame = (519.5 10; 50 50); clipsToBounds = YES; opaque = NO; layer = <CALayer: 0x600001b50e20>>

如果您只需要更改集合中的一个或多个项目,请尝试使用:

func reloadItems(at: [IndexPath])

【讨论】:

在我的应用程序中,我需要重新加载集合视图中的所有可见单元格,因此我必须获取可见单元格的所有索引并调用 reloadItems,但结果是一样的,弹出框移动到另一个细胞。我想了解为什么代码在 iOS 12 中有效,而在 iOS 13 中无效。

以上是关于移动时附加到 UICollectionView 中的单元格的弹出框被称为此集合视图的 ReloadData的主要内容,如果未能解决你的问题,请参考以下文章

将附加视图插入从 Xib 加载的 UICollectionView

UICollectionView在初始化的时候移动到某个距离

UICollectionView 固定背景视图

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

UICollectionView 将项目移动到新部分

滑动时如何使用左右移动的项目制作 UICollectionview?