用于粘性标头的UICollectionViewFlowLayout-当内容偏移量超过设备高度时,标头消失

时间:2018-09-29 18:58:41

标签: swift uicollectionview uicollectionviewflowlayout

我正在实现粘性标头UICollectionViewFlowLayout。向下跟踪时,我可以拉伸标题,向上跟踪时,它可以折叠到最小高度40pt(加上安全区域)。

我遇到的问题是,在向上跟踪时,一旦scrollView的y偏移量超过设备高度,标题就会消失。我只能假定这与collectionView中单元格出队的方式有关,但我不知道如何阻止它。

这是布局代码:

class StretchyHeaderLayout: UICollectionViewFlowLayout {

    internal var headerHeight: StickyHeaderHeight! = StickyHeaderHeight(
        collapsed: 40.0,
        home: 250.0,
        expanded: UIScreen.main.bounds.height)

    fileprivate var cache = [UICollectionViewLayoutAttributes]()

    override func prepare() {
        super.prepare()

        cache.removeAll()

        guard let collectionView = collectionView else { return }

        let sections = [Int](0..<collectionView.numberOfSections)
        for section in sections {
            let items = [Int](0..<collectionView.numberOfItems(inSection: section))
            for item in items {
                let indexPath = IndexPath(item: item, section: section)
                if let attribute = layoutAttributesForItem(at: indexPath) {
                    cache.append(attribute)
                }
            }
        }

        if let header = layoutAttributesForSupplementaryView(ofKind: StretchyCollectionHeaderKind, at: IndexPath(item: 0, section: 0)) {
            cache.append(header)
        }
    }

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

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

        let visibleAttributes = cache.filter { rect.contains($0.frame) || rect.intersects($0.frame) }
        guard let collectionView = collectionView else { return visibleAttributes }

        guard let header = cache.filter({ $0.representedElementKind == StretchyCollectionHeaderKind }).first else { return visibleAttributes }

        header.frame = headerFrame(for: collectionView.contentOffset)
        header.zIndex = 1000

        return visibleAttributes
    }

    func headerFrame(for contentOffset: CGPoint) -> CGRect {

        guard let collectionView = collectionView else { return .zero }

        let x = CGFloat(0)
        let y = contentOffset.y
        let width = collectionView.frame.size.width
        let height = max(min(headerHeight.home, headerHeight.expanded) - y, headerHeight.collapsed + UIScreen.safeArea.top)

        print(min(headerHeight.home, headerHeight.expanded) - y.rounded(), max(min(headerHeight.home, headerHeight.expanded) - y, headerHeight.collapsed + UIScreen.safeArea.top).rounded(), headerHeight.collapsed + UIScreen.safeArea.top, height.rounded(), y.rounded())

        return CGRect(x: x, y: y, width: width, height: height)
    }

    override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        let attributes = super.layoutAttributesForItem(at: indexPath as IndexPath)?.copy() as! UICollectionViewLayoutAttributes
        guard collectionView != nil else { return attributes }

        attributes.frame.origin.y =  headerHeight.home + attributes.frame.origin.y

        return attributes
    }

    override func layoutAttributesForSupplementaryView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        return UICollectionViewLayoutAttributes(forSupplementaryViewOfKind: StretchyCollectionHeaderKind, with: indexPath)
    }

    override var collectionViewContentSize: CGSize {
        get {
            guard let collectionView = collectionView else { return .zero }

            let numberOfSections = collectionView.numberOfSections
            let lastSection = numberOfSections - 1
            let numberOfItems = collectionView.numberOfItems(inSection: lastSection)
            let lastItem = numberOfItems - 1

            guard let lastCell = layoutAttributesForItem(at: IndexPath(item: lastItem, section: lastSection)) else { return .zero }

            return CGSize(width: collectionView.frame.width, height: lastCell.frame.maxY + sectionInset.bottom)
        }
    }
}

要调试,我尝试通过以下方法确定标题是否不再存在:

guard let header = cache.filter({ $0.representedElementKind == StretchyCollectionHeaderKind }).first else { return visibleAttributes }

但是它总是会通过警戒条件。

以下是打印语句的输出,其中标题在iPhone 10(812.0pt屏幕)上消失了:

header offset | header height x3 | scrollView.contentOffset.y

-560.0       84.0  84.0  84.0         810.0
-561.0       84.0  84.0  84.0         811.0
-561.0       84.0  84.0  84.0         811.0
-561.0       84.0  84.0  84.0         811.0
-561.0       84.0  84.0  84.0         811.0
-561.0       84.0  84.0  84.0         811.0
-561.0       84.0  84.0  84.0         811.0
-562.0       84.0  84.0  84.0         812.0 <— disappears here
-562.0       84.0  84.0  84.0         812.0
-563.0       84.0  84.0  84.0         813.0
-564.0       84.0  84.0  84.0         814.0
-564.0       84.0  84.0  84.0         814.0

0 个答案:

没有答案