在swift中使用UICollectionView粘性标头
我正在尝试创建一个粘性补充标头,它始终保持在顶部并且不会响应滚动事件。到目前为止我发现的解决方案仍然对bounch滚动做出反应并使用自定义flowlayout进行修复,这可能也是我的问题的解决方案。
I'm trying to create a sticky supplementary header, which stays in top all the time and wont response to scrolling events. The solutions I found so far still react on bounch scrolling and are fixed using a custom flowlayout, which will probably be the fix for mine issue as well.
我想要的原因这样,标题是在其他地方使用,应该是可重用的。我希望这可以通过这种方式解决,我不必创建一个单独的视图。
The reason I want it this way, is that the header is used on other places and should be reusable. I'm hoping this could be solved this way and I don't have to create a separated view.
正如我在Swift中这样做的,它会很棒在Swift中有一个例子。
As I'm doing this in Swift, it would be great to have an example in Swift.
我找到的最终解决方案:
使用此自定义流布局可以修复此粘性标题:
Using this custom flow layout it was possible to fix this sticky header:
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
}
}
要像传统一样创建标题粘贴的布局,请更改此行:
To create a layout where the headers are sticky like traditional, change this line:
origin.y = min(contentOffset.y, (CGRectGetMaxY(lastCellAttrs.frame) - headerHeight))
到此行:
origin.y = min(max(contentOffset.y, (CGRectGetMinY(firstCellAttrs.frame) - headerHeight)), (CGRectGetMaxY(lastCellAttrs.frame) - headerHeight))
希望这对其他人有用!
更新
更新以修复崩溃(感谢Robert Atkins!)和Swift 1.2的一些更新
Updated to fix a crash (thanks to Robert Atkins!) and some updates to Swift 1.2
tvOS& iOS 9
tvOS和iOS 9引入了属性 sectionHeadersPinToVisibleBounds
可以使用
tvOS and iOS 9 introduced the property sectionHeadersPinToVisibleBounds
which can be used