如何使补充视图漂浮在UICollectionView作为节头中的UITableView普通样式做(H

2019-06-21 06:51发布

我竭力要实现与“浮动节头”的效果UICollectionView 。 东西是一帆风顺足够UITableView (使用默认行为UITableViewStylePlain )中似乎是不可能的UICollectionView没有大量艰苦的工作。 我缺少明显的?

苹果提供了关于如何实现这一目标的文档。 看来,一个有子类UICollectionViewLayout并实现自定义布局只是为了达到这种效果。 这需要相当多的工作中,实现了以下方法:

方法重写

每一个布局对象应该实现以下方法:

collectionViewContentSize
layoutAttributesForElementsInRect:
layoutAttributesForItemAtIndexPath:
layoutAttributesForSupplementaryViewOfKind:atIndexPath: (if your layout supports supplementary views)
layoutAttributesForDecorationViewOfKind:atIndexPath: (if your layout supports decoration views)
shouldInvalidateLayoutForBoundsChange:

但它不是我清楚如何使补充视图浮置于单元上方,并“粘”在视图的顶部,直到达到下一节。 有没有在布局属性为这个标志?

我会用UITableView ,但我需要创建这是很容易与集合视图实现藏品的一个相当复杂的层次。

任何指导或示例代码将不胜感激!

Answer 1:

在iOS9,苹果还跟添加一个简单的财产UICollectionViewFlowLayout称为sectionHeadersPinToVisibleBounds

有了这个,你可以使头部漂浮一样,在表视图。

let layout = UICollectionViewFlowLayout()
layout.sectionHeadersPinToVisibleBounds = true
layout.minimumInteritemSpacing = 1
layout.minimumLineSpacing = 1
super.init(collectionViewLayout: layout)


Answer 2:

无论是实现以下的委托方法:

– collectionView:layout:sizeForItemAtIndexPath:
– collectionView:layout:insetForSectionAtIndex:
– collectionView:layout:minimumLineSpacingForSectionAtIndex:
– collectionView:layout:minimumInteritemSpacingForSectionAtIndex:
– collectionView:layout:referenceSizeForHeaderInSection:
– collectionView:layout:referenceSizeForFooterInSection:

在具有您的视图控制器:cellForItemAtIndexPath方法(只返回正确的值)。 或者,而是采用委托方法,您也可以直接在你的布局对象,如设置这些值[layout setItemSize:size];

使用这两种方法将使您,当您设置自定义布局他们删除了代码,而不是IB设置您的设置。 记得加上<UICollectionViewDelegateFlowLayout>到您的.h文件中,太!

创建一个新的子类UICollectionViewFlowLayout ,叫它任何你想要的,并确保.h文件有:

#import <UIKit/UIKit.h>

@interface YourSubclassNameHere : UICollectionViewFlowLayout

@end

里面的实现文件确保它有以下几点:

- (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;

}

选择“自定义”,在界面生成器的流式布局,选择刚才创建的“YourSubclassNameHere”类。 跑!

(注:上面的代码可能不尊重contentInset.bottom值,或尤其是大或小的页脚的物体,或具有0的对象,但没有页脚集合。)



Answer 3:

下面是我对,我觉得这比什么瞥见上面简单得多。 简约的主要来源是,我不是子类流布局,而不是我自己的滚动布局(容易得多,如果你问我)。

请注意,我假设你已经能够实现自己的自定义的UICollectionViewLayout将显示单元和头不带浮动来实现。 一旦你有一个书面的实施,只有这样,下面的代码作任何意义。 再次,这是因为OP是关于浮动头部分具体要求。

一些奖金:

  1. 我浮动两个头,不只是一个
  2. 头推先前头闪开
  3. 瞧,快捷!

注意:

  1. supplementaryLayoutAttributes包含不带浮动实现所有头属性
  2. 我使用这个代码prepareLayout ,因为我做的所有计算前期。
  3. 不要忘了覆盖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
}


Answer 4:

如果你有一个头查看要固定你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

请参阅: https://gist.github.com/4613982



Answer 5:

如果已经设置流布局StoryboardXib文件,然后试试这个,

(collectionView.collectionViewLayout as? UICollectionViewFlowLayout)?.sectionHeadersPinToVisibleBounds = true


Answer 6:

如果有人正在寻找在Objective-C的解决方案,把这个在viewDidLoad中:

    UICollectionViewFlowLayout *flowLayout = 
    (UICollectionViewFlowLayout*)_yourcollectionView.collectionViewLayout;
    [flowLayout setSectionHeadersPinToVisibleBounds:YES];


Answer 7:

@iPrabu有一个优秀的答案与sectionHeadersPinToVisibleBounds 。 我就补充一点,你可以设置在Interface Builder这个属性,以及:

  1. 选择在文档导航器中的流动布局对象。 (如果崩溃,展开它首先用在编辑器的左下角的工具栏按钮。)

  1. 打开身份检查,并与关键路径添加一个用户自定义运行属性sectionHeadersPinToVisibleBounds布尔类型,并选中复选框。

的缺省头视图具有透明背景。 你可能希望把它(部分)不透明或添加模糊效果图。



Answer 8:

我遇到了同样的问题,发现这个在我的谷歌搜索结果。 首先,我要感谢cocotutch分享他的解决方案。 不过,我想我的UICollectionView 水平滚动和头粘在屏幕的左侧,所以我不得不改变溶液一点。

基本上我只是改变了这一点:

        CGFloat headerHeight = CGRectGetHeight(layoutAttributes.frame);
        CGPoint origin = layoutAttributes.frame.origin;
        origin.y = MIN(
                       MAX(
                           contentOffset.y,
                           (CGRectGetMinY(firstCellAttrs.frame) - headerHeight)
                           ),
                       (CGRectGetMaxY(lastCellAttrs.frame) - headerHeight)
                       );

        layoutAttributes.zIndex = 1024;
        layoutAttributes.frame = (CGRect){
            .origin = origin,
            .size = layoutAttributes.frame.size
        };

为此:

        if (self.scrollDirection == UICollectionViewScrollDirectionVertical) {
            CGFloat headerHeight = CGRectGetHeight(layoutAttributes.frame);
            CGPoint origin = layoutAttributes.frame.origin;
            origin.y = MIN(
                           MAX(contentOffset.y, (CGRectGetMinY(firstCellAttrs.frame) - headerHeight)),
                           (CGRectGetMaxY(lastCellAttrs.frame) - headerHeight)
                           );

            layoutAttributes.zIndex = 1024;
            layoutAttributes.frame = (CGRect){
                .origin = origin,
                .size = layoutAttributes.frame.size
            };
        } else {
            CGFloat headerWidth = CGRectGetWidth(layoutAttributes.frame);
            CGPoint origin = layoutAttributes.frame.origin;
            origin.x = MIN(
                           MAX(contentOffset.x, (CGRectGetMinX(firstCellAttrs.frame) - headerWidth)),
                           (CGRectGetMaxX(lastCellAttrs.frame) - headerWidth)
                           );

            layoutAttributes.zIndex = 1024;
            layoutAttributes.frame = (CGRect){
                .origin = origin,
                .size = layoutAttributes.frame.size
            };
        }

请参阅: https://gist.github.com/vigorouscoding/5155703或http://www.vigorouscoding.com/2013/03/uicollectionview-with-sticky-headers/



Answer 9:

vigorouscoding的代码运行此。 但是该代码没有考虑sectionInset。

所以我改变了这种代码为垂直滚动

origin.y = MIN(
              MAX(contentOffset.y, (CGRectGetMinY(firstCellAttrs.frame) - headerHeight)),
              (CGRectGetMaxY(lastCellAttrs.frame) - headerHeight)
           );

origin.y = MIN(
           MAX(contentOffset.y, (CGRectGetMinY(firstCellAttrs.frame) - headerHeight - self.sectionInset.top)),
           (CGRectGetMaxY(lastCellAttrs.frame) - headerHeight + self.sectionInset.bottom)
           );

如果你们想要的水平滚动代码,请参阅代码aove。



Answer 10:

我已经添加了对样品github上 ,这是非常简单的,我想。

基本策略是提供在边界更改将使自定义布局,并提供该拥抱当前边界的补充视图布局属性。 正如其他建议。 我希望的代码是非常有用的。



Answer 11:

有一个在cocotouch的职位的错误。 当在段和段尾没有项目被设置为不显示,节头会去外面集合视图和用户将无法看到它。

其实变化:

if (numberOfItemsInSection > 0) {
    firstObjectAttrs = [self layoutAttributesForItemAtIndexPath:firstObjectIndexPath];
    lastObjectAttrs = [self layoutAttributesForItemAtIndexPath:lastObjectIndexPath];
} else {
    firstObjectAttrs = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader
                                                            atIndexPath:firstObjectIndexPath];
    lastObjectAttrs = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionFooter
                                                           atIndexPath:lastObjectIndexPath];
}

成:

if (numberOfItemsInSection > 0) {
    firstObjectAttrs = [self layoutAttributesForItemAtIndexPath:firstObjectIndexPath];
    lastObjectAttrs = [self layoutAttributesForItemAtIndexPath:lastObjectIndexPath];
} else {
    firstObjectAttrs = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader
                                                            atIndexPath:firstObjectIndexPath];
    lastObjectAttrs = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionFooter
                                                           atIndexPath:lastObjectIndexPath];
    if (lastObjectAttrs == nil) {
        lastObjectAttrs = firstObjectAttrs;
    }
}

将解决这个问题。



Answer 12:

VCollectionViewGridLayout做粘头。 它是基于垂直滚动简单的网格布局TLIndexPathTools 。 尝试运行粘头样本项目。

这种布局也有大大优于批量更新动画行为UICollectionViewFlowLayout 。 有一对夫妇提供的样本项目,让您切换之间的两个布局展示的提高。



文章来源: How to make Supplementary View float in UICollectionView as Section Headers do in UITableView plain style