【问题标题】:Custom UICollectionViewLayout and floating supplementary header views crash自定义 UICollectionViewLayout 和浮动补充标题视图崩溃
【发布时间】:2015-05-11 21:39:39
【问题描述】:

我正在尝试创建一个代表网格的UICollectionView,并且我想在顶部边缘创建一个在 y 轴上浮动的标尺(浮动标题视图)。 所以,就像在UITableViews 中一样,这个视图基本上应该只是随着屏幕顶部的内容滚动。我希望描述足够清楚。理论讲了这么多,让我们进入实践部分!

这是我现在正在使用的:

  • 我注册了 GridRulerView 类,它是 UICollectionReusableView 与集合视图。
  • 我为正确种类的补充视图和没有标题的 CGSizeZero 返回正确的大小。
  • 我实现了viewForSupplementaryElementOfKind::

    func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView
    {
        var reusableHeaderView : UICollectionReusableView!
        if kind == GridRulerView.Kind{
            reusableHeaderView = collectionView.dequeueReusableSupplementaryViewOfKind(GridRulerView.Kind, withReuseIdentifier: GridRulerView.Kind, forIndexPath: indexPath) as! UICollectionReusableView
    
        }
        return reusableHeaderView
    }
    

我有一个自定义的UICollectionViewLayout,其中包含以下内容 layoutAttributesForElementsInRect:方法:

override func layoutAttributesForElementsInRect(rect: CGRect) -> [AnyObject]? {

        var attribs = [UICollectionViewLayoutAttributes]()
        for (indexPath, attrs) in self.attributes{
            if CGRectIntersectsRect(rect, attrs.frame) {
                attribs.append(attrs)
            }
        }

        var headerAttributes = GridViewRulerAttributes(forSupplementaryViewOfKind: GridRulerView.Kind, withIndexPath: NSIndexPath(forItem: 0, inSection: 0))
        headerAttributes.zIndex = 500
        headerAttributes.frame = CGRectMake(0, self.collectionView!.contentOffset.y, self.preCalculatedContentSize.width, 80)
        attribs.append(headerAttributes)

        return attribs

}

当我启动应用程序时,一切看起来都很棒,正是我想要的样子。 但是一旦我滚动 collectionView 超出它的界限(collectionView 的 contentOffset = (-0.5,-0.5)

*** -[UICollectionViewData validateLayoutInRect:] 中的断言失败,/SourceCache/UIKit/UIKit-3318.16.25/UICollectionViewData.m:426

有趣的是,如果我将 header view 属性的 frame 的 y 属性设置为 0 而不是变量 contentOffset.y(变为负数),一切正常。

有人知道为什么会这样吗?

编辑

这里有一个更丰富的错误信息:

*** 由于未捕获的异常“NSInternalInconsistencyException”而终止应用程序,原因:“索引路径 ({length = 2, path = 0 - 0}) 处的补充项目的布局属性已从索引路径更改为: ({length = 2) , 路径 = 0 - 0});元素种类:(GridRulerViewKind);帧 = (0 0; 425.227 24); zIndex = 500;索引路径:( {length = 2, path = 0 - 0});元素种类:(GridRulerViewKind);帧 = (0 -5; 425.227 24); zIndex = 500;不使布局无效'

【问题讨论】:

  • 我相信在*** Assertion... 下方的几行中有更多关于这次崩溃的信息。检查他们,他们可以给你一个提示。例如应该有类似:... *** Terminating app due to uncaught exception ... reason: ...

标签: ios swift uicollectionview uicollectionviewlayout


【解决方案1】:

为什么要传递 GridRulerView.Kind 来获取 ReuseIdentifier?

    reusableHeaderView = collectionView.dequeueReusableSupplementaryViewOfKind(GridRulerView.Kind, withReuseIdentifier: **GridRulerView.Kind**, forIndexPath: indexPath) as! UICollectionReusableView

应该如下。

var 补充视图 = collectionView.dequeueReusableSupplementaryViewOfKind(种类, withReuseIdentifier:Identifiers.HeaderIdentifier.rawValue, forIndexPath: indexPath) as UICollectionReusableView

【讨论】:

  • 我在GridViewRuler 类中添加了一个名为“Kind”的静态字符串属性。将kindreuseIdentifier 作为同一个字符串不会导致此问题 - 如果我错了,请纠正我...
【解决方案2】:

哇,实际上禁用了“所有异常”断点,并得到了更多信息性错误:

*** 由于未捕获的异常“NSInternalInconsistencyException”而终止应用程序,原因:“索引路径 ({length = 2, path = 0 - 0}) 处的补充项目的布局属性已从索引路径更改为: ({length = 2) , 路径 = 0 - 0});元素种类:(GridRulerViewKind);帧 = (0 0; 425.227 24); zIndex = 500;索引路径:( {length = 2, path = 0 - 0});元素种类:(GridRulerViewKind);帧 = (0 -5; 425.227 24); zIndex = 500;不使布局无效'

现在,我仍然不知道该做什么,也不知道该去哪里

“使布局无效”

然后在浏览类似问题时,我在 SO 上遇到了一个类似的问题,我偶然发现了这个简单的功能: 罢工>

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

果然,这似乎奏效了!没有崩溃了!

编辑

实际上,这导致集合视图布局在每次滚动时都无效,所以这似乎不是我正在寻找的解决方案......

编辑 #2

经过一番研究,我认为不可能创建一个在不使布局无效的情况下自行更改的补充视图(这似乎完全合乎逻辑 - 布局如何知道只更新标尺视图而不明确这么说?)。而且更新每个滚动条上的布局实在是太昂贵了。

所以我想我将创建一个完全独立于集合视图的单独视图,并简单地监听集合视图的滚动委托并根据滚动偏移量调整其框架。我认为这种方法在性能方面效率更高,并且同样易于实施。

【讨论】:

  • 你需要定义你需要在layoutAttributesForElementsInRect中布局哪个representedElementCategory
【解决方案3】:

解决方案

只需检查代表元素种类

class CustomFlowLayout: UICollectionViewFlowLayout {

    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        let attributesForElementsInRect = super.layoutAttributesForElements(in: rect)
        var newAttributesForElementsInRect = [UICollectionViewLayoutAttributes]()

        for attributes in attributesForElementsInRect! {

            if !(attributes.representedElementKind == UICollectionElementKindSectionHeader
                || attributes.representedElementKind == UICollectionElementKindSectionFooter) {

                // cells will be customise here, but Header and Footer will have layout without changes.
            }

            newAttributesForElementsInRect.append(attributes)
        }

        return newAttributesForElementsInRect
    }
}


class YourViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let headerNib = UINib.init(nibName: "HeaderCell", bundle: nil)
        collectionView.register(headerNib, forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "HeaderCell")

        let footerNib = UINib.init(nibName: "FooterCell", bundle: nil)
        collectionView.register(footerNib, forSupplementaryViewOfKind: UICollectionElementKindSectionFooter, withReuseIdentifier: "FooterCell")
    }


    func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {

        switch kind {
        case UICollectionElementKindSectionHeader:
            let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "HeaderCell", for: indexPath) as! HeaderCell
            return headerView
        case UICollectionElementKindSectionFooter:
            let footerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "FooterCell", for: indexPath) as! FooterCell
            return footerView
        default:
            return UICollectionReusableView()
        }
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
        return CGSize(width: collectionView.frame.width, height: 45)
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
        return CGSize(width: collectionView.frame.width, height: 25)
    }
}

【讨论】:

    猜你喜欢
    • 2015-12-22
    • 2013-02-20
    • 1970-01-01
    • 2013-09-09
    • 2011-12-25
    • 1970-01-01
    • 2021-01-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多