如何为固定宽度、支持动态类型的UICollectionViewCell实现动态高度?
可行解决方案步骤如下:
调整UICollectionView布局配置
你之前在sizeForItemAt写死了高度,导致动态类型变化时无法自适应,先修改布局配置开启自动高度计算:- 若使用
UICollectionViewFlowLayout,初始化时添加估算尺寸配置:
let layout = UICollectionViewFlowLayout() // 宽度设为你要的固定值,高度随便给一个估算值即可,实际会自动计算 layout.estimatedItemSize = CGSize(width: UIScreen.main.bounds.width - 50, height: 300) layout.minimumLineSpacing = 16 // 可根据需求调整行间距 collectionView.collectionViewLayout = layout- 修改
sizeForItemAt方法,固定宽度、高度设为自动:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { return CGSize( width: collectionView.frame.width - 50, height: UICollectionViewFlowLayout.automaticDimension ) }- 若使用
修正自定义Cell内部的Auto Layout约束(核心)
约束配置错误是你在Cell内无法自适应高度的核心原因,按以下规则加约束:- 所有UI元素必须添加到
UICollectionViewCell的contentView上,不要直接加在Cell本身,否则约束不会生效 - 容器UIView四边等于
contentView四边,边距设为0 - 顶部ImageView约束:
- 顶部、leading、trailing等于容器View对应边,边距按你的设计稿设置
- 加高度约束:
imageView.heightAnchor.constraint(equalTo: imageView.widthAnchor, multiplier: 0.7).isActive = true,正好满足你要的高度为Collection宽度0.7倍的需求 - 底部约束到下方HorizontalStackView的顶部,边距按设计稿设置
- 底部HorizontalStackView约束:
- leading、trailing等于容器View对应边,边距按设计稿设置
- 底部等于容器View的底部,边距按设计稿设置(必须加这个底部约束,系统才能正确计算Cell高度)
- alignment属性设为
.top,保证序号标签和垂直StackView顶部对齐 - 序号标签的抗压缩优先级、内容拥抱优先级设为高于垂直StackView内的标签,避免序号被挤压变形
- 垂直StackView内的所有标签配置:
numberOfLines = 0,允许多行显示- 字体使用动态类型适配:
label.font = UIFontMetrics(forTextStyle: .body).scaledFont(for: UIFont.systemFont(ofSize: 14)),数字可替换为你原来的字号
- 给contentView加固定宽度约束,避免自动计算时宽度跑偏:
// 在自定义Cell的初始化或awakeFromNib方法中添加 private var cellWidthConstraint: NSLayoutConstraint? override func awakeFromNib() { super.awakeFromNib() cellWidthConstraint = contentView.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.width - 50) cellWidthConstraint?.isActive = true } // 如果支持屏幕旋转,在控制器的屏幕旋转回调中更新约束的constant,之后调用布局刷新即可- 所有UI元素必须添加到
动态类型切换时自动刷新
若需要用户切换动态类型字号后即时生效,监听系统字号变更通知,刷新CollectionView布局:// 在控制器viewDidLoad中添加监听 NotificationCenter.default.addObserver( self, selector: #selector(contentSizeChanged), name: UIContentSizeCategory.didChangeNotification, object: nil ) @objc private func contentSizeChanged() { collectionView.collectionViewLayout.invalidateLayout() }
内容的提问来源于stack exchange,提问作者paprika
相关产品推荐
相关产品推荐

