简体   繁体   English

UICollectionView 和补充视图(标题)

[英]UICollectionView and Supplementary View (header)

Trying to add aa Supplementary view into my UICollectionView as a header.尝试将补充视图作为标题添加到我的UICollectionView中。 I'm having issues getting it to work.我在让它工作时遇到问题。

I use a custom UICollectionViewFlowLayout to return a contentSize that is always at least 1 pixel larger then the frame (I am using a UIFreshControl which will only work if the UICollectionView scrolls, and setting collectionView.contentSize directly does nothing) and to invalidateLayout on sectionInsert and itemSize changes:我使用自定义UICollectionViewFlowLayout返回始终比框架大至少 1 个像素的contentSize (我使用的UIFreshControl仅在UICollectionView滚动时才有效,并且直接设置collectionView.contentSize不执行任何操作) invalidateLayout sectionInsertitemSize变化:

-(void)setSectionInset:(UIEdgeInsets)sectionInset {
    if (UIEdgeInsetsEqualToEdgeInsets(super.sectionInset, sectionInset)) {
        return;
    }

    super.sectionInset = sectionInset;

    [self invalidateLayout];

}

-(void) setItemSize:(CGSize)itemSize {
    if (CGSizeEqualToSize(super.itemSize, itemSize)) {
        return;
    }

    super.itemSize = itemSize;

    [self invalidateLayout];
}

- (CGSize)collectionViewContentSize
{
    CGFloat height = [super collectionViewContentSize].height;

    // Always returns a contentSize larger then frame so it can scroll and UIRefreshControl will work
    if (height < self.collectionView.bounds.size.height) {
        height = self.collectionView.bounds.size.height + 1;
    }

    return CGSizeMake([super collectionViewContentSize].width, height);
}

I created a UICollectionReusableView class which is just a UIView with a UILabel :我创建了一个UICollectionReusableView类,它只是一个带有UILabelUIView

@implementation CollectionHeaderView

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        NSArray *arrayOfViews = [[NSBundle mainBundle] loadNibNamed:@"CollectionHeaderView" owner:self options:nil];

        if ([arrayOfViews count] < 1) {
            return nil;
        }

        if (![[arrayOfViews objectAtIndex:0] isKindOfClass:[UICollectionViewCell class]]) {
            return nil;
        }

        self = [arrayOfViews objectAtIndex:0];

        self.headerLabel.text = @"This is a header. There are many like it.";
        self.backgroundColor = [UIColor yellowColor];


    }
    return self;
}

Trying to implement it:试图实现它:

DatasetLayout *collectionViewFlowLayout = [[DatasetLayout alloc] init];
collectionViewFlowLayout.itemSize = CGSizeMake(360, 160);
collectionViewFlowLayout.scrollDirection = UICollectionViewScrollDirectionVertical;
collectionViewFlowLayout.sectionInset = UIEdgeInsetsMake(16, 16, 16, 16);
collectionViewFlowLayout.minimumInteritemSpacing = 16;
collectionViewFlowLayout.minimumLineSpacing = 16;
collectionViewFlowLayout.headerReferenceSize = CGSizeMake(0, 100);

UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:collectionViewFlowLayout];
collectionView.translatesAutoresizingMaskIntoConstraints = FALSE;
collectionView.backgroundColor = [UIColor yellowColor];
collectionView.delegate = self;
collectionView.dataSource = self;

I register the class:我注册了课程:

[self.collectionView registerClass:[CollectionHeaderView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"CollectionHeaderView"];

and implement the delegate:并实现委托:

-(UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath {

    CollectionHeaderView *headerView = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"CollectionHeaderView" forIndexPath:indexPath];

    headerView.headerLabel.text = @"Blarg!";

    return headerView;
}

The line线

collectionViewFlowLayout.headerReferenceSize = CGSizeMake(0, 100);

causes the error:导致错误:

*** Assertion failure in -[UICollectionView _createPreparedSupplementaryViewForElementOfKind:atIndexPath:withLayoutAttributes:], /SourceCache/UIKit_Sim/UIKit-2380.17/UICollectionView.m:1150
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UICollectionView dataSource is not set'

If I comment it out, it runs but no header.如果我将其注释掉,它会运行但没有标题。

What am I doing wrong or not implementing?我做错了什么或没有实施?

I faced with the similar problem, but my app crashed after I programmatically pop my UICollectionViewController.我遇到了类似的问题,但是在我以编程方式弹出 UICollectionViewController 后,我的应用程序崩溃了。 In some reason (I think it's just a bug in SDK) self.collectionView was alive after its' controller destroy, thus causing this failure:出于某种原因(我认为这只是 SDK 中的一个错误) self.collectionView 在其控制器销毁后仍处于活动状态,从而导致此故障:

*** Assertion failure in -[UICollectionView _createPreparedSupplementaryViewForElementOfKind:atIndexPath:withLayoutAttributes:applyAttributes:], /SourceCache/UIKit/UIKit-2935.137/UICollectionView.m:1305
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UICollectionView dataSource is not set'

The solution is just override -dealloc in UICollectionViewController and release self.collectionView manually.解决方案只是覆盖 UICollectionViewController 中的 -dealloc 并手动释放 self.collectionView 。 ARC code: ARC代码:

- (void)dealloc {

    self.collectionView = nil;
}

Hope this will save time for somebody.希望这会为某人节省时间。

The answers in this topic are quite old, and do not work in iOS8 and iOS9.这个话题的答案很旧,在iOS8和iOS9中不起作用。 If you are still having the described issue, check out the following topic: UICollectionView and Supplementary View crash如果您仍然遇到所描述的问题,请查看以下主题: UICollectionView 和补充视图崩溃

EDIT :编辑

Here is the answer, in case the link becomes invalid:这是答案,以防链接无效:

If you are using custom layout in your collection view, check if its datasource is nil in:如果您在集合视图中使用自定义布局,请检查其数据源是否为零:

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect

The result should look something like this:结果应该是这样的:

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
    if (self.collectionView.dataSource != nil) {
        // Your layout code    
        return array;
    }
    return nil;
}

This change resolves the following issue:此更改解决了以下问题:

  • Assertion failure in -[UICollectionView _createPreparedSupplementaryViewForElementOfKind:atIndexPath:withLayoutAttributes:applyAttributes:]断言失败 -[UICollectionView _createPreparedSupplementaryViewForElementOfKind:atIndexPath:withLayoutAttributes:applyAttributes:]

You can also check the following topic, however it didn't resolve anything in my case:您还可以查看以下主题,但在我的情况下它没有解决任何问题:

Removing empty space, if the section header is hidden in the UICollectionView 删除空白空间,如果部分标题隐藏在 UICollectionView 中

Hope it will help you, and you won't waste as much time as I did.希望它会帮助你,你不会像我那样浪费时间。

I cleaned up my code and removed the UICollectionView I had created in IB and created it all in code.我清理了我的代码并删除了我在 IB 中创建的 UICollectionView 并在代码中创建了它。 I ran the it again and got a different error and realized I didn't set the delegate or dataSource in IB.我再次运行它并得到一个不同的错误并意识到我没有在 IB 中设置委托或数据源。 I did:我做了:

self.collectionView.delegate = self;
self.collectionView.datasource = self;

But that must not have been good enough.但这一定还不够好。

So with UICollectionView created in IB and not setting the delgate/datasource in IB, but rather in the code:因此,在 IB 中创建 UICollectionView 而不是在 IB 中设置 delgate/datasource,而是在代码中:

[self.view bringSubviewToFront:self.collectionView];
self.collectionView.collectionViewLayout = collectionViewFlowLayout;
self.collectionView.backgroundColor = [UIColor orangeColor];
self.collectionView.delegate = self;
self.collectionView.dataSource = self;

[self.collectionView registerClass:[DatasetCell class] forCellWithReuseIdentifier:@"cvCell"];

[self.collectionView registerClass:[CollectionHeaderView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"CollectionHeaderView"];

was an issue.是个问题。 I redid it to create the UICollectionView all in code:我重做了它以在代码中创建 UICollectionView :

UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:collectionViewFlowLayout];
collectionView.translatesAutoresizingMaskIntoConstraints = FALSE;
collectionView.backgroundColor = [UIColor yellowColor];
collectionView.delegate = self;
collectionView.dataSource = self;

[collectionView registerClass:[DatasetCell class] forCellWithReuseIdentifier:@"cvCell"];

[collectionView registerClass:[CollectionHeaderView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"CollectionHeaderView"];

[self.view addSubview:collectionView];

self.collectionView = collectionView;

and i get a different error:我得到一个不同的错误:

*** Assertion failure in -[UICollectionView _dequeueReusableViewOfKind:withIdentifier:forIndexPath:], /SourceCache/UIKit_Sim/UIKit-2380.17/UICollectionView.m:2249
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'could not dequeue a view of kind: UICollectionElementKindSectionHeader with identifier CollectionHeaderView - must register a nib or a class for the identifier or connect a prototype cell in a storyboard'

which I'll try to figure out.我会试着弄清楚。

EDIT:编辑:

figured it out, I was registering the header incorrectly:想通了,我错误地注册了标题:

[collectionView registerClass:[CollectionHeaderView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"CollectionHeaderView"];

switched to:切换到:

UINib *headerNib = [UINib nibWithNibName:@"CollectionHeaderView" bundle:nil];

[collectionView registerNib:headerNib forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"CollectionHeaderView"];

and everything works.一切正常。 Not entirely sure how the registerClass: wasn't working though.不完全确定registerClass:是如何工作的。

If you look at the error message, it says that the data source is not set.如果您查看错误消息,它表示未设置数据源。 This should fix it:这应该解决它:

self.collectionView.dataSource = self;

Make sure your view controller implements the data source protocol:确保您的视图控制器实现了数据源协议:

YourViewController : UIViewController<UICollectionViewDataSource>

I also got this annoying error:我也遇到了这个烦人的错误:

*** Assertion failure in -[UICollectionView _createPreparedSupplementaryViewForElementOfKind:atIndexPath:withLayoutAttributes:applyAttributes:], /SourceCache/UIKit/UIKit-3347.44/UICollectionView.m:1400
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UICollectionView dataSource is not set'

and I found some people solving this error by doing我发现有些人通过做来解决这个错误

- (void)dealloc {
    self.collectionView = nil;
}

or in swift:或迅速:

deinit {
    collectionView = nil
}

however neither of these solved my crash.但是这些都没有解决我的崩溃。 I tried a few things out and the following "hack" solved this annoying bug:我尝试了一些东西,下面的“hack”解决了这个烦人的错误:

func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
    if collectionView.dataSource != nil {
        return someHeaderSize
    } else {
        return CGSizeZero
    }
}

This answer is similar to the others where you create a method that checks for the data source, but it's a bit simpler.这个答案类似于您创建一个检查数据源的方法的其他答案,但它更简单一些。 It seems the issue is related to the collection view's layout sticking around after the collection has been released.这个问题似乎与集合发布后集合视图的布局仍然存在有关。 So I zeroed out the layout's properties as below and the error disappeared.所以我将布局的属性归零,如下所示,错误消失了。

deinit {
    let layout = self.collectionView.collectionViewLayout as! UICollectionViewFlowLayout
    layout.estimatedItemSize = CGSizeZero
    layout.sectionInset = UIEdgeInsetsZero
    layout.headerReferenceSize = CGSizeZero
}

I was having the same issue and similarly, I was not registering the header correctly.我遇到了同样的问题,同样,我没有正确注册标题。 there are several threads on here that all suggest using a Nib to define the header but I have done it differently and is working fine.这里有几个线程都建议使用 Nib 来定义标题,但我做了不同的事情并且工作正常。

I have a custom header SizeCollectionHeader, which inherits the UICollectionReusableView.我有一个自定义标头 SizeCollectionHeader,它继承了 UICollectionReusableView。 It is registered like this.它是这样注册的。

    [self.collectionView registerClass:[GRSizeCollectionHeader class] forSupplementaryViewOfKind:@"UICollectionElementKindSectionHeader"
                                                                             withReuseIdentifier:@"SupplementaryViewIdentifier"];

and is working fine.并且工作正常。

my initial problem was that had a random "Kind" value like this.我最初的问题是有一个像这样的随机“种类”值。

    [self.collectionView registerClass:[GRSizeCollectionHeader class] forSupplementaryViewOfKind:@"SupplementaryViewKind"
                                                                             withReuseIdentifier:@"SupplementaryViewIdentifier"];

This will crash.这会崩溃。

So I just wanted to comment for anybody that is looking here that you don't need to use a nib.所以我只是想评论一下这里的任何人,你不需要使用笔尖。

Try this :试试这个 :

self.collectionView?.dataSource = nil
self.collectionView = nil

It worked for me ;)它对我有用;)

I kept getting the same error.我一直收到同样的错误。 I had set up the CustomHeaderView for my collectionView.我已经为我的 collectionView 设置了 CustomHeaderView。 I had class of the Collection Reusable View to my CustomHeaderView and i had set the identifier.我有一个集合可重用视图的类到我的 CustomHeaderView 并且我已经设置了标识符。 I 1/2 an hour trying to figure out why this was failing.我每小时 1/2 次试图找出失败的原因。

Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'could not dequeue a view of kind: UICollectionElementKindSectionFooter with identifier SectionHeader - must register a nib or a class for the identifier or connect a prototype cell in a storyboard'由于未捕获的异常“NSInternalInconsistencyException”而终止应用程序,原因:“无法使类型的视图出列:UICollectionElementKindSectionFooter 与标识符 SectionHeader - 必须为标识符注册一个笔尖或类,或连接故事板中的原型单元格”

You need to read the logs carefully...lesson learned.你需要仔细阅读日志......吸取了教训。

'could not dequeue a view of kind: UICollectionElementKindSectionFooter ' 不能出列类型的视图:UICollectionElementKindSectionFooter

The reason is because In storyboard - in the collectionView Identity Inspector I had checked both Accessories: Section Header and Section Footer.原因是在情节提要中 - 在 collectionView Identity Inspector 中,我检查了两个附件:节页眉和节页脚。 I only needed section header.我只需要节标题。 Simply uncheck footer...build and run..BOOM!只需取消选中页脚...构建并运行...BOOM!

A iOS9 bug, In viewcontroller's dealloc set layer delegate nil.一个 iOS9 错误,在视图控制器的 dealloc 设置层委托 nil。

- (void)dealloc{

    if ([[[UIDevice currentDevice] systemVersion] hasPrefix:@"9"]) {
        self.collectionView.layer.delegate = nil;
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM