我正在实现粘性标头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