自定义UICollectionViewLayout和浮动补充标头视图崩溃

时间:2015-03-10 12:35:46

标签: ios swift uicollectionview uicollectionviewlayout

我正在尝试创建一个表示网格的UICollectionView,我想在顶部边缘创建一个标尺(浮动标题视图),它在y轴上浮动。 所以,就像在UITableView中一样,这个视图基本上应该只是滚动屏幕顶部的内容。我希望这些描述足够清楚。对于理论而言,让我们进入实践部分!

以下是我现在正在使用的内容:

  • 我注册了类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

有趣的是,如果我将标题视图属性的框架的y属性设置为0而不是变量contentOffset.y(变为负数),一切正常。

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

编辑

以下是更具信息性的错误消息:

  

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

无效

3 个答案:

答案 0 :(得分:1)

哇,实际上是残疾人"所有异常"断点并得到一个更具信息性的错误:

  

***由于未捕获的异常终止应用程序' NSInternalInconsistencyException',原因:'索引路径({length = 2,path = 0 - 0})上的补充项目的布局属性已从索引路径:({length = 2,path = 0 - 0});元素种类:(GridRulerViewKind); frame =(0 0; 425.227 24); zIndex = 500;索引路径:({length = 2,path = 0 - 0});元素种类:(GridRulerViewKind); frame =(0 -5; 425.227 24); zIndex = 500;没有使布局失效'

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

  

"使布局无效"

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

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

果然,这似乎有效!不再崩溃了!

编辑

实际上,这会导致集合视图布局在每个卷轴上失效,所以这似乎不是我正在寻找的解决方案......

编辑#2

经过一番研究后,我不认为有可能创建一个自行更改而不会使布局无效的补充视图(哪种看似完全合乎逻辑 - 布局如何知道更新标尺没有明确说明的观点?)。并且更新每个卷轴上的布局太昂贵了。

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

答案 1 :(得分:1)

解决方案

只需检查presentedElementKind

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

答案 2 :(得分:-2)

为什么要为ReuseIdentifier传递GridRulerView.Kind?

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

应如下所示。

  

var supplementView =   collectionView.dequeueReusableSupplementaryViewOfKind(实物,   withReuseIdentifier:Identifiers.HeaderIdentifier.rawValue,   forIndexPath:indexPath)as UICollectionReusableView