当前位置: 首页 > 知识库问答 >
问题:

UICollectionView -动态单元格高度?[重复]

盖昊东
2023-03-14

我需要显示一组具有不同高度的collectionViewCells。视图太复杂,我不想手动计算预期的高度。我想强制自动布局来计算单元格高度

cellForItemAtIndexPath之外调用dequeueReusableCellWithReuseIdfier会破坏ColltionView并导致其崩溃

另一个问题是单元格不在单独的xib中,所以我无法手动实例化临时单元格并将其用于高度计算。

对此有什么解决办法吗?

public func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
    
    var cell = collectionView.dequeueReusableCellWithReuseIdentifier(cellIdentifier, forIndexPath: indexPath) as UICollectionViewCell
    configureCell(cell, item: items[indexPath.row])
    
    cell.contentView.setNeedsLayout()
    cell.contentView.layoutIfNeeded()
    
    return cell.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
}

编辑:

一旦dequeueReusableCellWithReuseIdfier被调用,就会发生崩溃。如果我不调用该方法并返回大小,则一切正常,单元格显示为没有计算大小的单元格。

流布局中不支持负数或零大小

2015-01-26 18:24:34.231 [13383:9752256] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 1 beyond bounds [0 .. 0]'
*** First throw call stack:
(
    0   CoreFoundation                      0x00000001095aef35 __exceptionPreprocess + 165
    1   libobjc.A.dylib                     0x0000000109243bb7 objc_exception_throw + 45
    2   CoreFoundation                      0x0000000109499f33 -[__NSArrayM objectAtIndex:] + 227
    3   UIKit                               0x0000000107419d9c -[UICollectionViewFlowLayout _getSizingInfos] + 842
    4   UIKit                               0x000000010741aca9 -[UICollectionViewFlowLayout _fetchItemsInfoForRect:] + 526
    5   UIKit                               0x000000010741651f -[UICollectionViewFlowLayout prepareLayout] + 257
    6   UIKit                               0x000000010742da10 -[UICollectionViewData _prepareToLoadData] + 67
    7   UIKit                               0x00000001074301c6 -[UICollectionViewData layoutAttributesForItemAtIndexPath:] + 44
    8   UIKit                               0x00000001073fddb1 -[UICollectionView _dequeueReusableViewOfKind:withIdentifier:forIndexPath:viewCategory:] + 248
    9                                       0x00000001042b824c _TFC1228BasePaginatingViewController14collectionViewfS0_FTCSo16UICollectionView6layoutCSo22UICollectionViewLayout22sizeForItemAtIndexPathCSo11NSIndexPath_VSC6CGSize + 700
    10                                     0x00000001042b83d4 _TToFC1228BasePaginatingViewController14collectionViewfS0_FTCSo16UICollectionView6layoutCSo22UICollectionViewLayout22sizeForItemAtIndexPathCSo11NSIndexPath_VSC6CGSize + 100
    11  UIKit                               0x0000000107419e2e -[UICollectionViewFlowLayout _getSizingInfos] + 988
    12  UIKit                               0x000000010741aca9 -[UICollectionViewFlowLayout _fetchItemsInfoForRect:] + 526
    13  UIKit                               0x000000010741651f -[UICollectionViewFlowLayout prepareLayout] + 257
    14  UIKit                               0x000000010742da10 -[UICollectionViewData _prepareToLoadData] + 67
    15  UIKit                               0x000000010742e0e9 -[UICollectionViewData validateLayoutInRect:] + 54
    16  UIKit                               0x00000001073f67b8 -[UICollectionView layoutSubviews] + 170
    17  UIKit                               0x0000000106e3c973 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 521
    18  QuartzCore                          0x0000000106b0fde8 -[CALayer layoutSublayers] + 150
    19  QuartzCore                          0x0000000106b04a0e _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 380
    20  QuartzCore                          0x0000000106b0487e _ZN2CA5Layer28layout_and_display_if_neededEPNS_11TransactionE + 24
    21  QuartzCore                          0x0000000106a7263e _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 242
    22  QuartzCore                          0x0000000106a7374a _ZN2CA11Transaction6commitEv + 390
    23  QuartzCore                          0x0000000106a73db5 _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv + 89
    24  CoreFoundation                      0x00000001094e3dc7 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 23
    25  CoreFoundation                      0x00000001094e3d20 __CFRunLoopDoObservers + 368
    26  CoreFoundation                      0x00000001094d9b53 __CFRunLoopRun + 1123
    27  CoreFoundation                      0x00000001094d9486 CFRunLoopRunSpecific + 470
    28  GraphicsServices                    0x000000010be869f0 GSEventRunModal + 161
    29  UIKit                               0x0000000106dc3420 UIApplicationMain + 1282
    30                                      0x000000010435c709 main + 169
    31  libdyld.dylib                       0x000000010a0f2145 start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException

共有3个答案

翟善
2023-03-14

我们可以在没有xib的情况下保持集合视图单元的动态高度(仅使用故事板)。

- (CGSize)collectionView:(UICollectionView *)collectionView
                  layout:(UICollectionViewLayout*)collectionViewLayout
  sizeForItemAtIndexPath:(NSIndexPath *)indexPath {

        NSAttributedString* labelString = [[NSAttributedString alloc] initWithString:@"Your long string goes here" attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:17.0]}];
        CGRect cellRect = [labelString boundingRectWithSize:CGSizeMake(cellWidth, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin context:nil];

        return CGSizeMake(cellWidth, cellRect.size.height);
}

确保 IB 中的线路数应为 0。

张茂勋
2023-03-14

我刚刚在一个UICollectionView上遇到了这个问题,我解决它的方式与上面的答案类似,但是是以一种纯UICollectionView的方式。

>

  • 创建一个自定义 UICollectionViewCell,其中包含要填充它以使其动态的任何内容。我为它创建了自己的.xib,因为它似乎是最简单的方法。

    在其中添加约束。允许从上到下计算单元格。如果你没有考虑到所有的高度,那么重新调整大小是没有用的。假设顶部有一个视图,下面有一个标签,下面还有另一个标签。您需要将单元顶部的约束连接到该视图的顶部,然后将视图的底部连接到第一个标签的顶部,将第一个标签的底部连接到第二个标签的顶部,将第二个标签的底部连接到单元的底部。

    加载。并在< code>viewDidLoad上将它注册到collectionView

    let nib = UINib(nibName: CustomCellName, bundle: nil)
    self.collectionView!.registerNib(nib, forCellWithReuseIdentifier: "customCellID")`
    

    将该 xib 的第二个副本加载到类中并将其存储为属性,以便可以使用它来确定该单元的大小

    let sizingNibNew = NSBundle.mainBundle().loadNibNamed(CustomCellName, owner: CustomCellName.self, options: nil) as NSArray
    self.sizingNibNew = (sizingNibNew.objectAtIndex(0) as? CustomViewCell)!
    

    在视图控制器中实现< code > uicollectionviewlayoutdelegate 。重要的方法称为< code > sizeForItemAtIndexPath 。在该方法中,您需要从与indexPath中的单元格相关联的数据源中提取数据。然后配置sizingCell并调用< code > preferredLayoutSizeFittingSize 。该方法返回一个CGSize,它由从< code > self . sizing cell . preferredlayoutsizefittingsize(targetSize)返回的宽度减去内容插入数和高度组成。

    override func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
        guard let data = datasourceArray?[indexPath.item] else {
            return CGSizeZero
        }
        let sectionInset = self.collectionView?.collectionViewLayout.sectionInset
        let widthToSubtract = sectionInset!.left + sectionInset!.right
    
        let requiredWidth = collectionView.bounds.size.width
    
    
        let targetSize = CGSize(width: requiredWidth, height: 0)
    
        sizingNibNew.configureCell(data as! CustomCellData, delegate: self)
        let adequateSize = self.sizingNibNew.preferredLayoutSizeFittingSize(targetSize)
        return CGSize(width: (self.collectionView?.bounds.width)! - widthToSubtract, height: adequateSize.height)
     }
    

    在自定义单元格本身的类中,您需要覆盖awakeFromNib,并告诉contentView

     override func awakeFromNib() {
        super.awakeFromNib()
        self.contentView.autoresizingMask = [UIViewAutoresizing.FlexibleHeight]
     }
    

    在自定义单元格中覆盖布局子视图

     override func layoutSubviews() {
       self.layoutIfNeeded()
      }
    

    在自定义单元的类中,实现首选布局大小适合大小。这是您需要对正在布置的项目进行任何欺骗的地方。如果它是一个标签,你需要告诉它应该是什么它的首选MaxWidth。

    func preferredLayoutSizeFittingSize(_ targetSize: CGSize)-> CGSize {
    
        let originalFrame = self.frame
        let originalPreferredMaxLayoutWidth = self.label.preferredMaxLayoutWidth
    
    
        var frame = self.frame
        frame.size = targetSize
        self.frame = frame
    
        self.setNeedsLayout()
        self.layoutIfNeeded()
        self.label.preferredMaxLayoutWidth = self.questionLabel.bounds.size.width
    
    
        // calling this tells the cell to figure out a size for it based on the current items set
        let computedSize = self.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
    
        let newSize = CGSize(width:targetSize.width, height:computedSize.height)
    
        self.frame = originalFrame
        self.questionLabel.preferredMaxLayoutWidth = originalPreferredMaxLayoutWidth
    
        return newSize
    }
    

    所有这些步骤都应该给你正确的大小。如果你得到0或其他奇怪的数字,你没有正确设置你的约束。

  • 岳出野
    2023-03-14

    这是Ray Wenderlich的教程,向您展示了如何使用AutoLayout来动态调整UITableViewCell的大小。我认为UICollettionViewCell也是如此。

    不过,基本上,您最终会将原型单元出队并进行配置,然后获取它的高度。读完这篇文章后,我决定不实现这个方法,只写一些清晰、明确的调整代码。

    以下是我认为整篇文章的“秘诀”:

    - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
        return [self heightForBasicCellAtIndexPath:indexPath];
    }
    
    - (CGFloat)heightForBasicCellAtIndexPath:(NSIndexPath *)indexPath {
        static RWBasicCell *sizingCell = nil;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            sizingCell = [self.tableView dequeueReusableCellWithIdentifier:RWBasicCellIdentifier];
        });
    
        [self configureBasicCell:sizingCell atIndexPath:indexPath];
        return [self calculateHeightForConfiguredSizingCell:sizingCell];
    }
    
    - (CGFloat)calculateHeightForConfiguredSizingCell:(UITableViewCell *)sizingCell {
        [sizingCell setNeedsLayout];
        [sizingCell layoutIfNeeded];
    
        CGSize size = [sizingCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
        return size.height + 1.0f; // Add 1.0f for the cell separator height
    }
    

    一旦你做到了这一点,像下面这样的代码将让你继续下去:

    //  ViewController.m
    #import "ViewController.h"
    #import "CollectionViewCell.h"
    @interface ViewController () <UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout> {
    
    }
    @property (weak, nonatomic) IBOutlet CollectionViewCell *cell;
    @property (weak, nonatomic) IBOutlet UICollectionView   *collectionView;
    @end
    @implementation ViewController
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.view.backgroundColor = [UIColor lightGrayColor];
        [self.collectionView registerNib:[UINib nibWithNibName:@"CollectionViewCell" bundle:nil] forCellWithReuseIdentifier:@"cell"];
    }
    - (void)viewDidAppear:(BOOL)animated {
        [super viewDidAppear:animated];
        NSLog(@"viewDidAppear...");
    }
    - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
        return 1;
    }
    - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
        return 50;
    }
    - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section {
        return 10.0f;
    }
    - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section {
        return 10.0f;
    }
    - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
        return [self sizingForRowAtIndexPath:indexPath];
    }
    - (CGSize)sizingForRowAtIndexPath:(NSIndexPath *)indexPath {
        static NSString *title                  = @"This is a long title that will cause some wrapping to occur. This is a long title that will cause some wrapping to occur.";
        static NSString *subtitle               = @"This is a long subtitle that will cause some wrapping to occur. This is a long subtitle that will cause some wrapping to occur.";
        static NSString *buttonTitle            = @"This is a really long button title that will cause some wrapping to occur.";
        static CollectionViewCell *sizingCell   = nil;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            sizingCell                          = [[NSBundle mainBundle] loadNibNamed:@"CollectionViewCell" owner:self options:nil][0];
        });
        [sizingCell configureWithTitle:title subtitle:[NSString stringWithFormat:@"%@: Number %d.", subtitle, (int)indexPath.row] buttonTitle:buttonTitle];
        [sizingCell setNeedsLayout];
        [sizingCell layoutIfNeeded];
        CGSize cellSize = [sizingCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
        NSLog(@"cellSize: %@", NSStringFromCGSize(cellSize));
        return cellSize;
    }
    - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
        static NSString *title                  = @"This is a long title that will cause some wrapping to occur. This is a long title that will cause some wrapping to occur.";
        static NSString *subtitle               = @"This is a long subtitle that will cause some wrapping to occur. This is a long subtitle that will cause some wrapping to occur.";
        static NSString *buttonTitle            = @"This is a really long button title that will cause some wrapping to occur.";
        CollectionViewCell *cell                = [collectionView dequeueReusableCellWithReuseIdentifier:@"cell" forIndexPath:indexPath];
        [cell configureWithTitle:title subtitle:[NSString stringWithFormat:@"%@: Number %d.", subtitle, (int)indexPath.row] buttonTitle:buttonTitle];
        return cell;
    }
    @end
    

    上面的代码(以及一个非常基本的< code > UICollectionViewCell 子类和相关的XIB)给了我这个:

     类似资料:
    • 嗨,在我的应用程序中,我有一个在Tabview中显示带有描述的prodcuts的要求。为了实现这一点,我在Tabview cell content视图上添加了两个satckviews。两个stackviews都保存标签,一个标签有prodcut名称,另一个标签有description。在应用程序发布时,应用程序的单元格高度默认为100,但当我选择单元格时,我想在单元格中显示完整的描述以及产品名称,

    • 问题内容: 我有一个UICollectionView,它从包含标签的可重用单元格加载单元格。数组为该标签提供内容。我可以使用sizeToFit轻松根据内容宽度调整标签宽度的大小。但是我无法制作适合标签的单元格。 这是代码 问题答案: 作为回报,文字大小

    • 场景- > TableView高度是使用纵横比相对于superview的高度动态设置的。 TableViewCell的高度是根据表格视图的高度计算的: 问题 - 最初在委托方法中没有正确计算表格视图高度。但在滚动单元格后正确计算。 尝试的解决方案: 在视图中重新加载。 调用 在单元格中进行. 有什么方法可以正确计算出这个表格的高度吗?

    • 我获取图像的方法是: 行高度设置为: 我也希望有图像被完美填充,很像instagram。我不希望图像的顶部和底部有酒吧。

    • 可变长度的文本数据正在注入到表视图单元格标签中。为了正确调整每个单元格高度的大小,我在中实现了: 这估计高度为88.0像素,如果更大,应该会自动调整高度大小。它非常适用于尚未滚动到的单元格(因为在滚动到单元格时被调用),但不适用于最初在加载数据时在屏幕上呈现的单元格。 我尝试过重新加载数据(正如许多其他资源中建议的那样): 在这两个和,它没有帮助。我迷路了…有人知道如何渲染最初在屏幕上加载的单元格

    • 当滚动垂直收集单元格并在其中显示水平收集视图时,我有一个性能问题。原因是单元的数据重用-在每个父单元重用周期重新加载水平收集视图。有人知道我怎样才能避免这么多的重装吗?