如何根据内容动态设置UICollectionViewCell的高度?
嘿,这个自适应高度的需求我太熟了!给你分享两种靠谱的实现方式,轻松搞定无图片缩高、标题自适应的问题:
方式一:自动布局 + UICollectionViewFlowLayout 自动计算(推荐)
这种方式不用手动算高度,靠系统自动布局来搞定,步骤很简单:
第一步:配置UICollectionViewFlowLayout
把FlowLayout的estimatedItemSize设为一个预估尺寸(比如你原来的固定高度就很合适),同时把itemSize设为UICollectionViewFlowLayout.automaticSize,告诉系统要自动计算cell高度:let layout = UICollectionViewFlowLayout() layout.estimatedItemSize = CGSize(width: view.frame.width, height: view.frame.height/1.5) // 用你原来的高度当预估值 layout.itemSize = UICollectionViewFlowLayout.automaticSize collectionView.collectionViewLayout = layout第二步:给Cell内的控件加完整的自动布局约束
假设你的Cell里有图片imageView、标题titleLabel,要保证从Cell的top到bottom有一条完整的约束链:imageView的top、leading、trailing和Cell内容视图对应边对齐,给它的高度约束设一个默认值(比如200),同时把约束优先级调低(比如.defaultLow),这样无图片时隐藏控件,高度会自动收缩。titleLabel要设置numberOfLines = 0让文本自动换行,然后和imageView的bottom对齐(有图片时),或者直接和Cell内容视图的top对齐(无图片时),leading、trailing和内容视图对齐,最后给titleLabel的bottom和内容视图的bottom加约束。
举个Cell里的约束代码示例:
// 在Cell的初始化方法里配置约束 imageView.translatesAutoresizingMaskIntoConstraints = false titleLabel.translatesAutoresizingMaskIntoConstraints = false titleLabel.numberOfLines = 0 contentView.addSubview(imageView) contentView.addSubview(titleLabel) NSLayoutConstraint.activate([ imageView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 16), imageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16), imageView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16), imageView.heightAnchor.constraint(equalToConstant: 200).priority = .defaultLow, // 低优先级适配无图片场景 titleLabel.topAnchor.constraint(equalTo: imageView.bottomAnchor, constant: 16), titleLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16), titleLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16), titleLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -16) ])然后在给Cell赋值时处理图片显示逻辑:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CustomCell", for: indexPath) as! CustomCell let model = dataSource[indexPath.item] if let image = model.image { cell.imageView.image = image cell.imageView.isHidden = false } else { cell.imageView.isHidden = true } cell.titleLabel.text = model.title return cell }
方式二:手动计算Cell高度(适合复杂布局)
如果你的布局特别复杂,自动布局搞不定,那就手动计算高度:
第一步:提前计算并缓存高度
在数据模型里加一个cachedHeight属性,每次数据变化时计算一次,避免在sizeForItemAt里重复计算影响性能:struct CellModel { var title: String var image: UIImage? var cachedHeight: CGFloat? mutating func calculateHeight(width: CGFloat) { var totalHeight: CGFloat = 0 // 图片高度:有图片就加图片高度+间距,没有就跳过 if image != nil { totalHeight += 200 + 16 // 图片高度 + 图片到标题的间距 } // 标题高度:计算文本在指定宽度下的换行高度 let titleFont = UIFont.systemFont(ofSize: 16) let titleRect = title.boundingRect(with: CGSize(width: width - 32, height: CGFloat.greatestFiniteMagnitude), options: .usesLineFragmentOrigin, attributes: [.font: titleFont], context: nil) totalHeight += titleRect.height + 32 // 标题高度 + 上下间距 cachedHeight = totalHeight } }第二步:在UICollectionViewDelegateFlowLayout里返回计算好的高度
实现sizeForItemAt方法,返回缓存的高度:func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { var model = dataSource[indexPath.item] let width = view.frame.width if model.cachedHeight == nil { model.calculateHeight(width: width) dataSource[indexPath.item] = model } return CGSize(width: width, height: model.cachedHeight ?? view.frame.height/1.5) // 用原来的高度当 fallback }
这两种方式都能实现你要的效果:无图片时高度自动缩短,标题长了会自动撑高Cell。我个人更推荐第一种,代码少还不容易出错~
内容的提问来源于stack exchange,提问作者Gino Sesia
相关产品推荐
相关产品推荐

