UICollectionView Sticky Section Header
程序员文章站
2022-07-14 18:40:52
...
UICollectionView Sticky Section Header
如果项目针对的是iOS9及其以后的系统,可直接使用系统的API来达到目的。在iOS9中,UICollectionViewFlowLayout
类引入了2个属性,sectionHeadersPinToVisibleBounds
和sectionFootersPinToVisibleBounds
,可以非常方便的做到固定不动的header和footer。
如果要兼容以前的iOS系统版本,就要自定义了,需继承UICollectionViewFlowLayout
以下的内容来自How to Add Sticky Section Headers to a Collection View
创建一个继承自UICollectionViewFlowLayout
的类,重写如下的方法:
shouldInvalidateLayout(forBoundsChange:)
layoutAttributesForElements(in:)
layoutAttributesForSupplementaryView(ofKind:at:)
代码如下:
class StickyHeadersCollectionViewFlowLayout: UICollectionViewFlowLayout {
// MARK: - Collection View Flow Layout Methods
// bounds改变是否需要更新
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
return true
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
guard let layoutAttributes = super.layoutAttributesForElements(in: rect) else { return nil }
// Helpers
let sectionsToAdd = NSMutableIndexSet() // 需要添加布局属性的section的集合
var newLayoutAttributes = [UICollectionViewLayoutAttributes]() // 新的布局属性的数组
for layoutAttributesSet in layoutAttributes {
if layoutAttributesSet.representedElementCategory == .cell { //cell类型
// Add Layout Attributes
newLayoutAttributes.append(layoutAttributesSet)
// Update Sections to Add
sectionsToAdd.add(layoutAttributesSet.indexPath.section)
} else if layoutAttributesSet.representedElementCategory == .supplementaryView { // 此处即header
// Update Sections to Add
sectionsToAdd.add(layoutAttributesSet.indexPath.section)
}
}
for section in sectionsToAdd {
let indexPath = IndexPath(item: 0, section: section)
if let sectionAttributes = self.layoutAttributesForSupplementaryView(ofKind: UICollectionElementKindSectionHeader, at: indexPath) {
newLayoutAttributes.append(sectionAttributes)
}
}
return newLayoutAttributes
}
// 使header固定的布局计算逻辑
override func layoutAttributesForSupplementaryView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
guard let layoutAttributes = super.layoutAttributesForSupplementaryView(ofKind: elementKind, at: indexPath) else { return nil }
guard let boundaries = boundaries(forSection: indexPath.section) else { return layoutAttributes }
guard let collectionView = collectionView else { return layoutAttributes }
// Helpers
let contentOffsetY = collectionView.contentOffset.y
var frameForSupplementaryView = layoutAttributes.frame
let minimum = boundaries.minimum - frameForSupplementaryView.height
let maximum = boundaries.maximum - frameForSupplementaryView.height
if contentOffsetY < minimum {
frameForSupplementaryView.origin.y = minimum
} else if contentOffsetY > maximum {
frameForSupplementaryView.origin.y = maximum
} else {
frameForSupplementaryView.origin.y = contentOffsetY
}
layoutAttributes.frame = frameForSupplementaryView
return layoutAttributes
}
// MARK: - Helper Methods
func boundaries(forSection section: Int) -> (minimum: CGFloat, maximum: CGFloat)? {
// Helpers
var result = (minimum: CGFloat(0.0), maximum: CGFloat(0.0))
// Exit Early
guard let collectionView = collectionView else { return result }
// Fetch Number of Items for Section 获取section有多少item
let numberOfItems = collectionView.numberOfItems(inSection: section)
// Exit Early
guard numberOfItems > 0 else { return result }
// 第一个item和最后一个item
if let firstItem = layoutAttributesForItem(at: IndexPath(item: 0, section: section)),
let lastItem = layoutAttributesForItem(at: IndexPath(item: (numberOfItems - 1), section: section)) {
result.minimum = firstItem.frame.minY
result.maximum = lastItem.frame.maxY
// Take Header Size Into Account
result.minimum -= headerReferenceSize.height
result.maximum -= headerReferenceSize.height
// Take Section Inset Into Account
result.minimum -= sectionInset.top
result.maximum += (sectionInset.top + sectionInset.bottom)
}
return result
}
}
其效果如下:
在Customising an iOS UICollectionView Layout中,介绍了sticker footer,效果如下:
源码地址,注意在Xcode9中运行会报错,参考UICollectionView exception in UICollectionViewLayoutAttributes from iOS7
:
与
Cell Prefetching
有关,在storyboard中取消勾选即可
推荐阅读
-
LongListSelector锁定组头(sticky header )之我的实现
-
h5中的结构元素header、nav、article、aside、section、footer介绍
-
UICollectionView Sticky Section Header
-
UICollectionView 设置section的背景色
-
第七步:html布局div(header/nav/section/footer)
-
怎样在UICollectionView中添加Header和footer
-
h5中的结构元素header、nav、article、aside、section、footer介绍
-
HTML5 里 section article 什么区别?header footer nav 该怎么理解?
-
LongListSelector锁定组头(sticky header )之我的实现
-
HTML5 里 section article 什么区别?header footer nav 该怎么理解?