You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

UICollectionView动态单元格尺寸调整失效,关联布局时图片显示异常求助

UICollectionView动态尺寸单元格布局问题修复

问题描述

我尝试实现支持单元格动态尺寸的UICollectionView,单元格内包含imageView和文本标签,整体功能可正常运行,但无法根据item尺寸自动调整每行展示的item数量,每行始终仅显示1个item。

现有效果

  • 关联collectionLayout时的输出效果:
    关联布局时效果
  • 未关联collectionLayout时可得到预期布局,但图片无法正常显示:
    未关联布局时效果

核心问题原因

  • 布局逻辑冲突:同时在collectionLayout的didSet中设置了estimatedItemSize = .automaticSize,又实现了sizeForItemAt代理手动计算尺寸,两个逻辑互斥导致布局计算异常
  • 单元格内部控件宽度设置错误:将image和label的最大宽度设置为接近屏幕宽度,导致单元格被撑满整行,自然一行只能显示1个item
  • 图片控件缺少正确的缩放配置,导致图片显示变形或不展示
  • 原有尺寸计算将单元格高度写死为正方形,无法适配不同长度的文本和不同比例的图片

修复方案

1. 修改ViewController代码

import UIKit

class ViewController: UIViewController {
    //MARK: - Outlets
    @IBOutlet weak var collectionViewOutlet: UICollectionView!
    // 删掉didSet里的estimatedItemSize配置,避免和手动计算尺寸冲突
    @IBOutlet weak var collectionLayout: UICollectionViewFlowLayout!
    
    //MARK: - Vars
    let items: [Item] = [Item(title: "abscancnakcnaslcnajcacnajkcnajncajsncjasc", image: UIImage(named: "1.jpg")!), Item(title: "abscancnakcnaslcnajcacnajkcnajncajsncjascabscancnakcnaslcnajcacnajkcnajncajsncjascabscancnakcnaslcnajcacnajkcnajncajsncjascabscancnakcnaslcnajcacnajkcnajncajsncjasc", image: UIImage(named: "2.jpg")!), Item(title: "abscancnakcnaslcnajcacnajkcnajncajsncjascabscancnakcnaslcnajcacnajkcnajncajsncjascabscancnakcnaslcnajcacnajkcnajncajsncjascabscancnakcnaslcnajcacnajkcnajncajsncjascabscancnakcnaslcnajcacnajkcnajncajsncjascabscancnakcnaslcnajcacnajkcnajncajsncjascabscancnakcnaslcnajcacnajkcnajncajsncjascabscancnakcnaslcnajcacnajkcnajncajsncjasc", image: UIImage(named: "3.jpg")!),Item(title: "abscancnakcnaslcnajcacnajkcnajncajsncjasc", image: UIImage(named: "2.jpg")!), Item(title: "abscancnakcnaslcnajcacnajkcnajncajsncjascabscancnakcnaslcnajcacnajkcnajncajsncjascabscancnakcnaslcnajcacnajkcnajncajsncjascabscancnakcnaslcnajcacnajkcnajncajsncjasc", image: UIImage(named: "1.jpg")!), Item(title: "abscancnakcnaslcnajcacnajkcnajncajsncjascabscancnakcnaslcnajcacnajkcnajncajsncjascabscancnakcnaslcnajcacnajkcnajncajsncjascabscancnakcnaslcnajcacnajkcnajncajsncjascabscancnakcnaslcnajcacnajkcnajncajsncjascabscancnakcnaslcnajcacnajkcnajncajsncjascabscancnakcnaslcnajcacnajkcnajncajsncjascabscancnakcnaslcnajcacnajkcnajncajsncjasc", image: UIImage(named: "3.jpg")!)]

    //MARK: - Insets
    let itemsPerRow = 2
    let sectionInsets = UIEdgeInsets(top: 5.0, left: 5.0, bottom: 5.0, right: 5.0)
    // 可自行修改为项目中使用的label字体大小
    let labelFont = UIFont.systemFont(ofSize: 14)

    //MARK: - Life Cycle
    override func viewDidLoad() {
        super.viewDidLoad()
        collectionViewOutlet.register(UINib(nibName: "CollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "Cell")
    }
}

extension ViewController:UICollectionViewDelegate, UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return items.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionViewOutlet.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! CollectionViewCell
        cell.imageOutlet.image = items[indexPath.item].image
        cell.lblTitleOutlet.text = items[indexPath.item].title
        // 修改最大宽度为单个item的宽度,不要用整屏宽度
        let paddingSpace = sectionInsets.left * CGFloat(itemsPerRow + 1)
        let itemWidth = (collectionViewOutlet.bounds.width - paddingSpace) / CGFloat(itemsPerRow) - 5
        cell.imageMaxWidth = itemWidth
        cell.lblMaxWidth = itemWidth
        return cell
    }
}

//MARK: - Collection View Flowlayout
extension ViewController: UICollectionViewDelegateFlowLayout {
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        let paddingSpace = sectionInsets.left * CGFloat(itemsPerRow + 1)
        let availableWidth = view.frame.width - paddingSpace
        let widthForItem = availableWidth / CGFloat(itemsPerRow) - 5
        // 动态计算高度,适配不同文本长度和图片比例
        let item = items[indexPath.item]
        // 计算文本高度
        let labelHeight = item.title!.boundingRect(with: CGSize(width: widthForItem, height: CGFloat.greatestFiniteMagnitude), options: .usesLineFragmentOrigin, attributes: [.font: labelFont], context: nil).height
        // 按图片比例计算图片展示高度
        let imageRatio = item.image.size.height / item.image.size.width
        let imageHeight = widthForItem * imageRatio
        // 总高度=图片高度+文本高度+上下间距(可根据自己的约束调整间距值)
        let totalHeight = imageHeight + labelHeight + 10
        return CGSize(width: widthForItem, height: totalHeight)
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
        return sectionInsets
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return sectionInsets.left
    }
    
    // 补充同一行item之间的间距配置
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
        return sectionInsets.left
    }
}

2. 修改CollectionViewCell代码

import UIKit

class CollectionViewCell: UICollectionViewCell {
//MARK: - Outlets
    @IBOutlet weak var imageOutlet: UIImageView! {
        didSet {
            // 配置图片按比例缩放,不拉伸
            imageOutlet.contentMode = .scaleAspectFit
            imageOutlet.clipsToBounds = true
        }
    }
    
    @IBOutlet weak var lblTitleOutlet: UILabel! {
        didSet {
            // 支持文本多行展示
            lblTitleOutlet.numberOfLines = 0
        }
    }
    
    @IBOutlet private var imageMaxWidthConstraint: NSLayoutConstraint! {
         didSet {
             imageMaxWidthConstraint.isActive = false
         }
     }
    
    @IBOutlet private var lblMaxWidthConstraint: NSLayoutConstraint! {
         didSet {
            lblMaxWidthConstraint.isActive = false
         }
     }
     
     var imageMaxWidth: CGFloat? = nil {
         didSet {
             guard let maxWidth = imageMaxWidth else {
                 return
             }
             imageMaxWidthConstraint.isActive = true
             imageMaxWidthConstraint.constant = maxWidth
         }
     }
   
     var lblMaxWidth: CGFloat? = nil {
         didSet {
             guard let maxWidth = lblMaxWidth else {
                 return
             }
            lblMaxWidthConstraint.isActive = true
            lblMaxWidthConstraint.constant = maxWidth
         }
     }
    //MARK: - Awake
    override func awakeFromNib() {
        super.awakeFromNib()
        contentView.translatesAutoresizingMaskIntoConstraints = false
             
        NSLayoutConstraint.activate([
            contentView.leftAnchor.constraint(equalTo: leftAnchor),
            contentView.rightAnchor.constraint(equalTo: rightAnchor),
            contentView.topAnchor.constraint(equalTo: topAnchor),
            contentView.bottomAnchor.constraint(equalTo: bottomAnchor)
        ])
    }
}

3. Item数据模型代码无需修改

import Foundation
import UIKit

struct Item {
    var title: String?
    var image : UIImage
    init(title: String, image: UIImage) {
        self.title = title
        self.image = image
    }
}

内容的提问来源于stack exchange,提问作者Menaim

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.14 06:15:03