Ios UICollectionViewLayout';冻结';

Ios UICollectionViewLayout';冻结';,ios,swift3,uicollectionviewlayout,Ios,Swift3,Uicollectionviewlayout,我是一个斯威夫特/iOS noobie,我开始认为我已经把珠穆朗玛峰作为我的第一个任务。在任何情况下,我的应用程序都有一个基于滚动周的日历视图。我正在使用自定义的UICollectionViewLayout。当应用程序启动时,一切看起来都很好,我可以向下滚动24小时和几天(见第一张图) 当用户达到预定义单元格数(天)的末尾时,会向数据源添加更多单元格。我可以愉快地连续向右滚动,代码会根据需要添加更多单元格 问题是: 当我在数据源中添加任何附加数据(天)后停止滚动,然后重新开始滚动时,布局冻结在

我是一个斯威夫特/iOS noobie,我开始认为我已经把珠穆朗玛峰作为我的第一个任务。在任何情况下,我的应用程序都有一个基于滚动周的日历视图。我正在使用自定义的
UICollectionViewLayout
。当应用程序启动时,一切看起来都很好,我可以向下滚动24小时和几天(见第一张图)

当用户达到预定义单元格数(天)的末尾时,会向数据源添加更多单元格。我可以愉快地连续向右滚动,代码会根据需要添加更多单元格

问题是:

当我在数据源中添加任何附加数据(天)后停止滚动,然后重新开始滚动时,布局冻结在单个块中,只是四处浮动(参见第二幅图)

将单元正确放置(固定)到视图顶部不再有效(因为它只是前面提到的冻结单元块),并且不会显示新单元

奇怪的是,布局冻结后,终端继续从
UICollectionViewLayout
注销调试,将单元格放置在正确的位置,尽管
UICollectionViewController
似乎刚刚停止侦听

在“日期”单元格的终端中打印:
单元格属性:索引路径:({length=2,path=2-0});帧=(1033235;4750);zIndex=1024;

从视图层次结构调试:
(UICollectionView):集合视图布局:

从“日期”单元格的视图层次结构调试:

[更新]

嗨,Rob-我尝试剥离我的代码,然后还学习了一些教程,当我添加数据源(singleton)时,这些教程都以同样的问题结束-所以有一个热门的怀疑(!),但我就是想不出来。下面的代码基于教程示例,但显示了相同的问题

CollectionViewController:

private let reuseIdentifier = "cCell"

class CustomCollectionViewController: UICollectionViewController {

    let localItems = Items.items.getItems()


    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    override func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 6  }


    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return Items.items.getItemCount()
    }

    override func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {

        if (Items.items.getItemCount() - indexPath.row ) == 2 {
            Items.items.addItems()
            collectionView.reloadData()
            collectionView.collectionViewLayout.invalidateLayout()
        }
    }

    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> CustomCollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! CustomCollectionViewCell

        cell.textLabel.text = "SEC=\(indexPath.section),IXDI=\(indexPath.item)"

        return cell
    }
}
布局:

class CustomCollectionViewLayout: UICollectionViewLayout {

    let CELL_HEIGHT = 145.0
    let CELL_WIDTH = 145.0
    let STATUS_BAR = UIApplication.shared.statusBarFrame.height
    var cellAttrsDictionary = Dictionary<IndexPath, UICollectionViewLayoutAttributes>()
    var contentSize = CGSize.zero

    override var collectionViewContentSize : CGSize {
        return contentSize
    }

    override func invalidateLayout() {
        super.invalidateLayout()
        cellAttrsDictionary.removeAll()
    }

    override func prepare() {

        if (collectionView?.numberOfSections)! > 0 {
            for section in 0...collectionView!.numberOfSections-1 {

               if (collectionView?.numberOfItems(inSection: section))! > 0 {
                    for item in 0...collectionView!.numberOfItems(inSection: section)-1 {

                        let cellIndex = IndexPath(item: item, section: section)
                        let xPos = Double(item) * CELL_WIDTH
                        let yPos = Double(section) * CELL_HEIGHT

                        let cellAttributes = UICollectionViewLayoutAttributes(forCellWith: cellIndex)
                        cellAttributes.frame = CGRect(x: xPos, y: yPos, width: CELL_WIDTH, height: CELL_HEIGHT)

                        if section == 0 && item == 0 {
                            cellAttributes.zIndex = 4
                        } else if section == 0 {
                            cellAttributes.zIndex = 3
                        } else if item == 0 {
                            cellAttributes.zIndex = 2
                        } else {
                            cellAttributes.zIndex = 1
                        }

                        cellAttrsDictionary[cellIndex] = cellAttributes
                    }
                }
            }
        }

        let contentWidth = Double(collectionView!.numberOfItems(inSection: 0)) * CELL_WIDTH
        let contentHeight = Double(collectionView!.numberOfSections) * CELL_HEIGHT
        self.contentSize = CGSize(width: contentWidth, height: contentHeight)
    }

    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        var attributesInRect = [UICollectionViewLayoutAttributes]()

        for cellAttributes in cellAttrsDictionary.values {
            if rect.intersects(cellAttributes.frame) {
                attributesInRect.append(cellAttributes)
            }
        }
        return attributesInRect
    }

    override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        return cellAttrsDictionary[indexPath]!
    }

    override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
        return true
    }
}

在我的测试中,
willDisplay
中重新加载数据源似乎是问题的根源。通过
DispatchQueue.main.async{…}
问题似乎消失了

private var lastReloadedRow = 0

override func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
    if (Items.items.getItemCount() - indexPath.row) == 2 && lastReloadedRow < indexPath.row {
        lastReloadedRow = indexPath.row
        DispatchQueue.main.async {
            Items.items.addItems()
            collectionView.reloadData()
        }
    }
}

在我的测试中,
willDisplay
中重新加载数据源似乎是问题的根源。通过
DispatchQueue.main.async{…}
问题似乎消失了

private var lastReloadedRow = 0

override func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
    if (Items.items.getItemCount() - indexPath.row) == 2 && lastReloadedRow < indexPath.row {
        lastReloadedRow = indexPath.row
        DispatchQueue.main.async {
            Items.items.addItems()
            collectionView.reloadData()
        }
    }
}

如果没有代码,很难诊断这个问题。我建议您创建一个问题的解决方案,然后发布该MCVE的图像和代码。但是这里没有足够的信息让我们理解出了什么问题,我们肯定不想看到产生这些图像的所有代码。我们需要带代码的MCVE。谢谢你看这个Rob。关于MCVE的好建议。让我这么做。顺便说一句,如果你的收藏视图似乎没有更新,即使你有证据表明应用程序没有死锁,我会留意任何来自后台线程的意外UI更新(手动分派到后台队列的内容,或运行在后台队列上的
URLSession
完成处理程序中的内容)。请确保在任何后台线程上都没有任何
重新加载数据
或类似调用。谢谢Rob。到目前为止,所有内容都应该在主线程上。Rob-我使用代码MCVE进行了更新。希望您能够理解:o)如果没有代码,将很难诊断此问题。我建议您创建一个问题的解决方案,然后发布该MCVE的图像和代码。但是这里没有足够的信息让我们理解出了什么问题,我们肯定不想看到产生这些图像的所有代码。我们需要带代码的MCVE。谢谢你看这个Rob。关于MCVE的好建议。让我这么做。顺便说一句,如果你的收藏视图似乎没有更新,即使你有证据表明应用程序没有死锁,我会留意任何来自后台线程的意外UI更新(手动分派到后台队列的内容,或运行在后台队列上的
URLSession
完成处理程序中的内容)。请确保在任何后台线程上都没有任何
重新加载数据
或类似的调用。谢谢Rob。到目前为止,所有内容都应该在主线程上。Rob-我使用代码MCVE进行了更新。希望您能够理解这一点:o)协议似乎排除了感谢;但是,嘿,这是值得称赞的,伟大的工作。对额外的调整有很好的了解。协议似乎排除了感谢;但是,嘿,这是值得称赞的,伟大的工作。对附加调谐的良好拾取。
class CustomCollectionViewLayout: UICollectionViewLayout {

    let CELL_HEIGHT = 145.0
    let CELL_WIDTH = 145.0
    let STATUS_BAR = UIApplication.shared.statusBarFrame.height
    var cellAttrsDictionary = Dictionary<IndexPath, UICollectionViewLayoutAttributes>()
    var contentSize = CGSize.zero

    override var collectionViewContentSize : CGSize {
        return contentSize
    }

    override func invalidateLayout() {
        super.invalidateLayout()
        cellAttrsDictionary.removeAll()
    }

    override func prepare() {
        if collectionView!.numberOfSections > 0 {
            for section in 0 ..< collectionView!.numberOfSections {
                if collectionView!.numberOfItems(inSection: section) > 0 {
                    for item in 0 ..< collectionView!.numberOfItems(inSection: section) {
                        let cellIndex = IndexPath(item: item, section: section)
                        let xPos = Double(item) * CELL_WIDTH
                        let yPos = Double(section) * CELL_HEIGHT

                        let cellAttributes = UICollectionViewLayoutAttributes(forCellWith: cellIndex)
                        cellAttributes.frame = CGRect(x: xPos, y: yPos, width: CELL_WIDTH, height: CELL_HEIGHT)

                        cellAttrsDictionary[cellIndex] = cellAttributes
                    }
                }
            }
        }

        let contentWidth = Double(collectionView!.numberOfItems(inSection: 0)) * CELL_WIDTH
        let contentHeight = Double(collectionView!.numberOfSections) * CELL_HEIGHT
        contentSize = CGSize(width: contentWidth, height: contentHeight)
    }

    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        return cellAttrsDictionary.values.filter { $0.frame.intersects(rect) }
    }

    override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        return cellAttrsDictionary[indexPath]!
    }

    // override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
    //     return true
    // }
}