首页 > 解决方案 > 当调用此集合视图的 ReloadData 时,附加到 UICollectionView 中的单元格的弹出框移动

问题描述

我有一个用户可以单击的 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/diegossantos95/UICollectionViewPopoverBug

在存储库中重现的步骤:

  1. 单击集合单元格

  2. 弹出框打开

  3. 等待重新加载数据

谢谢,

迭戈。

标签: swiftuicollectionviewuikitpopoverreloaddata

解决方案


您可以使用从单元格的框架创建的临时 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()
    }
}

20 年 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)
}

推荐阅读