在swift中使用UICollectionView粘贴头

Ant*_*ine 15 ios uicollectionview uicollectionviewlayout swift tvos

我正在尝试创建一个粘性补充标题,它始终保持在顶部并且不会响应滚动事件.到目前为止我找到的解决方案仍然对bounch滚动做出反应,并使用自定义flowlayout进行修复,这可能也是我的问题的解决方案.

我想这样做的原因是标题在其他地方使用,应该是可重用的.我希望这可以通过这种方式解决,我不必创建一个单独的视图.

正如我在Swift中所做的那样,在Swift中有一个例子会很棒.

Dar*_*ari 65

最简单的iOS 9 +解决方案,因为它不需要编写UICollectionViewFlowLayout的子类.

在带有collectionView的viewController的viewDidLoad中使用以下代码:

let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout // casting is required because UICollectionViewLayout doesn't offer header pin. Its feature of UICollectionViewFlowLayout
layout?.sectionHeadersPinToVisibleBounds = true
Run Code Online (Sandbox Code Playgroud)

它也被@Antoine暗示.


Ant*_*ine 7

我找到的最终解决方案:

使用此自定义流布局,可以修复此粘性标头:

class StickyHeaderCollectionViewFlowLayout: UICollectionViewFlowLayout {

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

        var superAttributes: [UICollectionViewLayoutAttributes]? = super.layoutAttributesForElementsInRect(rect) as? [UICollectionViewLayoutAttributes]

        if superAttributes == nil {
            // If superAttributes couldn't cast, return
            return super.layoutAttributesForElementsInRect(rect)
        }

        let contentOffset = collectionView!.contentOffset
        var missingSections = NSMutableIndexSet()

        for layoutAttributes in superAttributes! {
            if (layoutAttributes.representedElementCategory == .Cell) {
                if let indexPath = layoutAttributes.indexPath {
                    missingSections.addIndex(layoutAttributes.indexPath.section)
                }
            }
        }

        for layoutAttributes in superAttributes! {
            if let representedElementKind = layoutAttributes.representedElementKind {
                if representedElementKind == UICollectionElementKindSectionHeader {
                    if let indexPath = layoutAttributes.indexPath {
                        missingSections.removeIndex(indexPath.section)
                    }
                }
            }
        }

        missingSections.enumerateIndexesUsingBlock { idx, stop in
            let indexPath = NSIndexPath(forItem: 0, inSection: idx)
            if let layoutAttributes = self.layoutAttributesForSupplementaryViewOfKind(UICollectionElementKindSectionHeader, atIndexPath: indexPath) {
                superAttributes!.append(layoutAttributes)
            }
        }

        for layoutAttributes in superAttributes! {
            if let representedElementKind = layoutAttributes.representedElementKind {
                if representedElementKind == UICollectionElementKindSectionHeader {
                    let section = layoutAttributes.indexPath!.section
                    let numberOfItemsInSection = collectionView!.numberOfItemsInSection(section)

                    let firstCellIndexPath = NSIndexPath(forItem: 0, inSection: section)!
                    let lastCellIndexPath = NSIndexPath(forItem: max(0, (numberOfItemsInSection - 1)), inSection: section)!


                    let (firstCellAttributes: UICollectionViewLayoutAttributes, lastCellAttributes: UICollectionViewLayoutAttributes) = {
                        if (self.collectionView!.numberOfItemsInSection(section) > 0) {
                            return (
                                self.layoutAttributesForItemAtIndexPath(firstCellIndexPath),
                                self.layoutAttributesForItemAtIndexPath(lastCellIndexPath))
                        } else {
                            return (
                                self.layoutAttributesForSupplementaryViewOfKind(UICollectionElementKindSectionHeader, atIndexPath: firstCellIndexPath),
                                self.layoutAttributesForSupplementaryViewOfKind(UICollectionElementKindSectionFooter, atIndexPath: lastCellIndexPath))
                        }
                        }()

                    let headerHeight = CGRectGetHeight(layoutAttributes.frame)
                    var origin = layoutAttributes.frame.origin

                    origin.y = min(contentOffset.y, (CGRectGetMaxY(lastCellAttributes.frame) - headerHeight))
                    // Uncomment this line for normal behaviour:
                    // origin.y = min(max(contentOffset.y, (CGRectGetMinY(firstCellAttributes.frame) - headerHeight)), (CGRectGetMaxY(lastCellAttributes.frame) - headerHeight))

                    layoutAttributes.zIndex = 1024
                    layoutAttributes.frame = CGRect(origin: origin, size: layoutAttributes.frame.size)
                }
            }
        }

        return superAttributes
    }

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

}
Run Code Online (Sandbox Code Playgroud)

要像传统一样创建标题粘贴的布局,请更改以下行:

origin.y = min(contentOffset.y, (CGRectGetMaxY(lastCellAttrs.frame) - headerHeight))
Run Code Online (Sandbox Code Playgroud)

到这一行:

origin.y = min(max(contentOffset.y, (CGRectGetMinY(firstCellAttrs.frame) - headerHeight)), (CGRectGetMaxY(lastCellAttrs.frame) - headerHeight))
Run Code Online (Sandbox Code Playgroud)

希望这对其他人有用!

更新

更新以修复崩溃(感谢Robert Atkins!)和Swift 1.2的一些更新

tvOS和iOS 9

tvOS和iOS 9引入了sectionHeadersPinToVisibleBounds可以使用的属性