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
相关产品推荐
相关产品推荐

