100*_*ams 65 iphone view supplementary uitableview uicollectionview
我正在努力实现"浮动节头"效果UICollectionView
.如果没有大量的努力工作,那么在UITableView
(默认行为UITableViewStylePlain
)中很容易做到的事情似乎是不可能的UICollectionView
.我错过了明显的吗?
Apple没有提供有关如何实现此目标的文档.似乎必须子类化UICollectionViewLayout
并实现自定义布局才能实现此效果.这需要相当多的工作,实现以下方法:
覆盖的方法
每个布局对象都应该实现以下方法:
collectionViewContentSize
layoutAttributesForElementsInRect:
layoutAttributesForItemAtIndexPath:
layoutAttributesForSupplementaryViewOfKind:atIndexPath: (if your layout supports supplementary views)
layoutAttributesForDecorationViewOfKind:atIndexPath: (if your layout supports decoration views)
shouldInvalidateLayoutForBoundsChange:
Run Code Online (Sandbox Code Playgroud)
然而,我不清楚如何使补充视图浮动在单元格上方并"粘贴"到视图的顶部,直到到达下一部分.布局属性中是否有这样的标志?
我会使用UITableView
但我需要创建一个相当复杂的集合层次结构,这可以通过集合视图轻松实现.
任何指导或示例代码将不胜感激!
iPr*_*abu 65
在iOS9中,Apple非常友好地在UICollectionViewFlowLayout
被调用中添加了一个简单的属性sectionHeadersPinToVisibleBounds
.
有了这个,你可以使表头视图中的标题浮动.
let layout = UICollectionViewFlowLayout()
layout.sectionHeadersPinToVisibleBounds = true
layout.minimumInteritemSpacing = 1
layout.minimumLineSpacing = 1
super.init(collectionViewLayout: layout)
Run Code Online (Sandbox Code Playgroud)
top*_*ide 47
实现以下委托方法:
– collectionView:layout:sizeForItemAtIndexPath:
– collectionView:layout:insetForSectionAtIndex:
– collectionView:layout:minimumLineSpacingForSectionAtIndex:
– collectionView:layout:minimumInteritemSpacingForSectionAtIndex:
– collectionView:layout:referenceSizeForHeaderInSection:
– collectionView:layout:referenceSizeForFooterInSection:
Run Code Online (Sandbox Code Playgroud)
在具有您的:cellForItemAtIndexPath
方法的视图控制器中(只返回正确的值).或者,您也可以直接在布局对象中设置这些值,而不是使用委托方法[layout setItemSize:size];
.
使用这些方法之一将使您能够在设置自定义布局时将代码设置为代码而不是IB.记得也要添加<UICollectionViewDelegateFlowLayout>
到.h文件中!
创建一个新的子类UICollectionViewFlowLayout
,随意调用它,并确保H文件具有:
#import <UIKit/UIKit.h>
@interface YourSubclassNameHere : UICollectionViewFlowLayout
@end
Run Code Online (Sandbox Code Playgroud)
在实现文件中,确保它具有以下内容:
- (NSArray *) layoutAttributesForElementsInRect:(CGRect)rect {
NSMutableArray *answer = [[super layoutAttributesForElementsInRect:rect] mutableCopy];
UICollectionView * const cv = self.collectionView;
CGPoint const contentOffset = cv.contentOffset;
NSMutableIndexSet *missingSections = [NSMutableIndexSet indexSet];
for (UICollectionViewLayoutAttributes *layoutAttributes in answer) {
if (layoutAttributes.representedElementCategory == UICollectionElementCategoryCell) {
[missingSections addIndex:layoutAttributes.indexPath.section];
}
}
for (UICollectionViewLayoutAttributes *layoutAttributes in answer) {
if ([layoutAttributes.representedElementKind isEqualToString:UICollectionElementKindSectionHeader]) {
[missingSections removeIndex:layoutAttributes.indexPath.section];
}
}
[missingSections enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:idx];
UICollectionViewLayoutAttributes *layoutAttributes = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader atIndexPath:indexPath];
[answer addObject:layoutAttributes];
}];
for (UICollectionViewLayoutAttributes *layoutAttributes in answer) {
if ([layoutAttributes.representedElementKind isEqualToString:UICollectionElementKindSectionHeader]) {
NSInteger section = layoutAttributes.indexPath.section;
NSInteger numberOfItemsInSection = [cv numberOfItemsInSection:section];
NSIndexPath *firstCellIndexPath = [NSIndexPath indexPathForItem:0 inSection:section];
NSIndexPath *lastCellIndexPath = [NSIndexPath indexPathForItem:MAX(0, (numberOfItemsInSection - 1)) inSection:section];
NSIndexPath *firstObjectIndexPath = [NSIndexPath indexPathForItem:0 inSection:section];
NSIndexPath *lastObjectIndexPath = [NSIndexPath indexPathForItem:MAX(0, (numberOfItemsInSection - 1)) inSection:section];
UICollectionViewLayoutAttributes *firstObjectAttrs;
UICollectionViewLayoutAttributes *lastObjectAttrs;
if (numberOfItemsInSection > 0) {
firstObjectAttrs = [self layoutAttributesForItemAtIndexPath:firstObjectIndexPath];
lastObjectAttrs = [self layoutAttributesForItemAtIndexPath:lastObjectIndexPath];
} else {
firstObjectAttrs = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader
atIndexPath:firstObjectIndexPath];
lastObjectAttrs = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionFooter
atIndexPath:lastObjectIndexPath];
}
CGFloat headerHeight = CGRectGetHeight(layoutAttributes.frame);
CGPoint origin = layoutAttributes.frame.origin;
origin.y = MIN(
MAX(
contentOffset.y + cv.contentInset.top,
(CGRectGetMinY(firstObjectAttrs.frame) - headerHeight)
),
(CGRectGetMaxY(lastObjectAttrs.frame) - headerHeight)
);
layoutAttributes.zIndex = 1024;
layoutAttributes.frame = (CGRect){
.origin = origin,
.size = layoutAttributes.frame.size
};
}
}
return answer;
}
- (BOOL) shouldInvalidateLayoutForBoundsChange:(CGRect)newBound {
return YES;
}
Run Code Online (Sandbox Code Playgroud)
在Interface Builder中为Flow Layout选择"Custom",选择刚刚创建的"YourSubclassNameHere"类.跑!
(注意:上面的代码可能不尊重contentInset.bottom值,或者特别是大或小的页脚对象,或者具有0个对象但没有页脚的集合.)
如果您有一个标题视图,您想要固定到UICollectionView的顶部,这是一个相对简单的方法.请注意,这应该尽可能简单 - 它假设您在单个部分中使用单个标头.
//Override UICollectionViewFlowLayout class
@interface FixedHeaderLayout : UICollectionViewFlowLayout
@end
@implementation FixedHeaderLayout
//Override shouldInvalidateLayoutForBoundsChange to require a layout update when we scroll
- (BOOL) shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
return YES;
}
//Override layoutAttributesForElementsInRect to provide layout attributes with a fixed origin for the header
- (NSArray *) layoutAttributesForElementsInRect:(CGRect)rect {
NSMutableArray *result = [[super layoutAttributesForElementsInRect:rect] mutableCopy];
//see if there's already a header attributes object in the results; if so, remove it
NSArray *attrKinds = [result valueForKeyPath:@"representedElementKind"];
NSUInteger headerIndex = [attrKinds indexOfObject:UICollectionElementKindSectionHeader];
if (headerIndex != NSNotFound) {
[result removeObjectAtIndex:headerIndex];
}
CGPoint const contentOffset = self.collectionView.contentOffset;
CGSize headerSize = self.headerReferenceSize;
//create new layout attributes for header
UICollectionViewLayoutAttributes *newHeaderAttributes = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader withIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]];
CGRect frame = CGRectMake(0, contentOffset.y, headerSize.width, headerSize.height); //offset y by the amount scrolled
newHeaderAttributes.frame = frame;
newHeaderAttributes.zIndex = 1024;
[result addObject:newHeaderAttributes];
return result;
}
@end
Run Code Online (Sandbox Code Playgroud)
请参阅:https://gist.github.com/4613982
这是我的看法,我认为这比上面的瞥见简单得多.简单的主要来源是我不是子类化流程布局,而是滚动我自己的布局(如果你问我,会更容易).
请注意我假设您已经能够实现自己的自定义UICollectionViewLayout
,它将显示单元格和标题而不实现浮动.一旦你编写了实现,只有这样,下面的代码才有意义.同样,这是因为OP专门询问浮动标题部分.
一些奖金:
注意:
supplementaryLayoutAttributes
包含所有未实现浮动的标头属性prepareLayout
,因为我提前完成所有计算.shouldInvalidateLayoutForBoundsChange
到真!// float them headers
let yOffset = self.collectionView!.bounds.minY
let headersRect = CGRect(x: 0, y: yOffset, width: width, height: headersHeight)
var floatingAttributes = supplementaryLayoutAttributes.filter {
$0.frame.minY < headersRect.maxY
}
// This is three, because I am floating 2 headers
// so 2 + 1 extra that will be pushed away
var index = 3
var floatingPoint = yOffset + dateHeaderHeight
while index-- > 0 && !floatingAttributes.isEmpty {
let attribute = floatingAttributes.removeLast()
attribute.frame.origin.y = max(floatingPoint, attribute.frame.origin.y)
floatingPoint = attribute.frame.minY - dateHeaderHeight
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
48994 次 |
最近记录: |