无法在 collectionView 内调用 dequeueReusableCellWithReuseIdentifier(collectionView:collectionViewLayout:si

Posted

技术标签:

【中文标题】无法在 collectionView 内调用 dequeueReusableCellWithReuseIdentifier(collectionView:collectionViewLayout:sizeForItemAtIndexPath)【英文标题】:Unable to call dequeueReusableCellWithReuseIdentifier inside of collectionView(collectionView: collectionViewLayout: sizeForItemAtIndexPath) 【发布时间】:2016-04-04 00:45:11 【问题描述】:

collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize 内部调用dequeueReusableCellWithReuseIdentifier 时应用崩溃。

注意:这个问题不是重复的,因为dequeueReusableCellWithReuseIdentifier 在 UICollectionView 的其他地方工作,只是不在特定函数内。

没有异常信息,Xcode只高亮汇编代码,所以不确定问题出在哪里。

代码如下。

目标是使单元格高度动态化,同时宽度与 UICollectionView 的宽度相匹配,就像 UITableView 中的单元格一样。

1) 为什么会在dequeueReusableCellWithReuseIdentifier 上崩溃?

2)如果你不能使用dequeueReusableCellWithReuseIdentifier,你还能如何动态确定单元格的固有高度?

3) 有没有更好的方法来模拟 UITableViewCell 的大小属性(即,与 superview 相同的宽度但动态高度)?关于这个主题有很多 SO 帖子,但没有一个提供非常干净的解决方案。

func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize 
    let cell = collectionView.dequeueReusableCellWithReuseIdentifier(MessageCellIdentifier, forIndexPath: indexPath) as! MessageCell

    let cellSize = CGSize(width: view.frame.width, height: cell.frame.height)
    return cellSize

【问题讨论】:

一个问题,你从来没有在sizeForItemAtIndexPath方法中使用cell参数?如果是这样,你为什么需要它...... @J.Wang 这是一个简化版本,cell 参数用于确定单元格高度。问题已更新。 ios Assertion Failure in UICollectionView的可能重复 @Basheer_CAD 不是骗子,请参阅问题进行澄清。 【参考方案1】:

更接近于理解崩溃

这是 UITVC 的 1:1 替代品,不会惹恼 Apple。

首先,这是使用回调的地方。

    // if delegate implements size delegate, query it for all items
    if (implementsSizeDelegate) 
        for (NSInteger item = 0; item < numberOfItems; item++) 
            NSIndexPath *indexPath = [NSIndexPath indexPathForItem:item inSection:(NSInteger)section];
            CGSize itemSize = implementsSizeDelegate ? [flowDataSource collectionView:self.collectionView layout:self sizeForItemAtIndexPath:indexPath] : self.itemSize;

            PSTGridLayoutItem *layoutItem = [layoutSection addItem];
            layoutItem.itemFrame = (CGRect).size=itemSize;
        

https://github.com/steipete/PSTCollectionView/blob/master/PSTCollectionView/PSTCollectionView.m

查看属性分配和最后一行...

- (id)dequeueReusableCellWithReuseIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath *)indexPath 
    // de-queue cell (if available)
    NSMutableArray *reusableCells = _cellReuseQueues[identifier];
    PSTCollectionViewCell *cell = [reusableCells lastObject];
    PSTCollectionViewLayoutAttributes *attributes = [self.collectionViewLayout layoutAttributesForItemAtIndexPath:indexPath];

    // ... IMPL ...

    [cell applyLayoutAttributes:attributes];

    return cell;

有很多代码要经过,但至少有一条路径可以让你陷入无限递归......

编辑

从我在下面发布的链接...

因为 collectionView:layout:sizeForItemAtIndexPath: 之前被调用过 cellForItemAtIndexPath:,所以我们需要初始化一个单元格并让 系统使用自动布局为我们计算高度。为了避免记忆 泄漏,我们使用字典来缓存屏幕外的单元格(不是 显示在屏幕上)

在collectionView:layout:sizeForItemAtIndexPath:中,先创建或检索一个cell

var cell: MyCollectionViewCell? = self.offscreenCells[reuseIdentifier] as? MyCollectionViewCell
if cell == nil 
    cell = NSBundle.mainBundle().loadNibNamed("MyCollectionViewCell", owner: self, options: nil)[0] as? MyCollectionViewCell
    self.offscreenCells[reuseIdentifier] = cell

单元格初始化后,其大小由 xib 文件中的大小决定,因此,我们需要在单元格和 layoutSubviews 中配置文本,这将让系统重新计算单元格的大小

// Config cell and let system determine size
cell!.configCell(titleData[indexPath.item], content: contentData[indexPath.item], titleFont: fontArray[indexPath.item] as String, contentFont: fontArray[indexPath.item] as String)
// Cell's size is determined in nib file, need to set it's width (in this case), and inside, use this cell's width to set label's preferredMaxLayoutWidth, thus, height can be determined, this size will be returned for real cell initialization
cell!.bounds = CGRectMake(0, 0, targetWidth, cell!.bounds.height)
cell!.contentView.bounds = cell!.bounds

// Layout subviews, this will let labels on this cell to set preferredMaxLayoutWidth
cell!.setNeedsLayout()
cell!.layoutIfNeeded()

一旦单元格更新,调用 var size = 单元格!.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize) 获取此单元格的大小。

在cellForItemAtIndexPath:中,cell也需要配置和布局它 子视图

原件

好吧,调用你命名的方法时它不会崩溃(一半),调用时它会崩溃

dequeueReusableCellWithReuseIdentifier(MessageCellIdentifier, 
    forIndexPath: indexPath) as! MessageCell

我看到的问题是您正在尝试调用一个方法,该方法需要您从中调用它的方法来给您答案。它应该会产生堆栈溢出。

我并不是说这是不可能的,但你没有足够的覆盖或我相信的正确方法。

查看https://github.com/honghaoz/Dynamic-Collection-View-Cell-With-Auto-Layout-Demo/blob/master/README.md 以获取您想要的示例,和/或发布更多代码。

【讨论】:

啊,有道理。所以你说 dequeueReusableCellWithReuseIdentifier 依赖于 collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize?这可以解释为什么它会崩溃。 :) 我相信确实如此,但我仍在寻找证据:) 我不太确定我的陈述在字面上是正确的,但更确定它在功能上是正确的,因为如果你沿着试图让那个电话工作的道路走下去,你将不会高兴。我一直都明白它实现了基于可见性和接近度的缓存,但是当它不知道高度的关键尺寸时它怎么能这样做...查看上面的编辑回答我刚刚发布了来自 gist 的中国同胞的博客,t the salient bits

以上是关于无法在 collectionView 内调用 dequeueReusableCellWithReuseIdentifier(collectionView:collectionViewLayout:si的主要内容,如果未能解决你的问题,请参考以下文章

更改 CollectionView 单元内的 UIView 宽度

在 CollectionView 中使用 SDWebImage 的疯狂内存问题(在 tableviewCell 内)

特定 CollectionViewCell 的 TableView 数据

collectionView cellForItemAt 没有被调用

在我的自定义 collectionView 布局中未调用 prepare()

在 collectionView 中加载图像