SwiftUI PreviewProvider中如何让UICollectionViewCell正确自适应尺寸
UICollectionViewCell SwiftUI Preview 高度自适应修复
问题根因
UICollectionViewCell 本身不提供基于内容的固有尺寸,默认intrinsicContentSize返回UIViewNoIntrinsicMetric(值为-1),其尺寸默认由 collection view 布局系统驱动,不会自动将内部contentView的约束向外传递。即使内部约束完整、给cell本身设置内容拥抱优先级,也无法让SwiftUI获取到正确的内容高度,最终预览会占满可用高度。
原代码还存在两个细节问题:
- 内部的UILabel没有关闭自动 autoresizing 掩码转换,会产生额外约束冲突
- cell与
contentView之间默认的约束是固定填充cell尺寸,不会反向让cell适配contentView大小
修复代码
通用预览包装器+Cell修正(推荐)
class MyCell: UICollectionViewCell { override init(frame: CGRect) { super.init(frame: frame) // 关键配置:允许contentView尺寸反向驱动cell大小 contentView.autoresizingMask = [] contentView.translatesAutoresizingMaskIntoConstraints = false let label1 = UILabel() label1.text = "First Label" label1.translatesAutoresizingMaskIntoConstraints = false let label2 = UILabel() label2.text = "Second Label" label2.translatesAutoresizingMaskIntoConstraints = false let blue = UIView() blue.backgroundColor = .systemBlue blue.translatesAutoresizingMaskIntoConstraints = false let stack = UIStackView(arrangedSubviews: [label1, label2, blue]) stack.axis = .vertical stack.spacing = 5 stack.alignment = .leading stack.translatesAutoresizingMaskIntoConstraints = false contentView.addSubview(stack) NSLayoutConstraint.activate([ stack.topAnchor.constraint(equalTo: contentView.topAnchor), stack.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), stack.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), stack.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), blue.heightAnchor.constraint(equalToConstant: 100), blue.widthAnchor.constraint(equalTo: stack.widthAnchor), // 关键:绑定contentView与cell的四边约束,让cell尺寸跟随contentView contentView.topAnchor.constraint(equalTo: topAnchor), contentView.bottomAnchor.constraint(equalTo: bottomAnchor), contentView.leadingAnchor.constraint(equalTo: leadingAnchor), contentView.trailingAnchor.constraint(equalTo: trailingAnchor) ]) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } struct UIViewPreview<View: UIView>: UIViewRepresentable { let view: View init(_ builder: @escaping () -> View) { view = builder() } // MARK: UIViewRepresentable func makeUIView(context: Context) -> UIView { let container = UIView() view.translatesAutoresizingMaskIntoConstraints = false container.addSubview(view) NSLayoutConstraint.activate([ view.leadingAnchor.constraint(equalTo: container.leadingAnchor), view.trailingAnchor.constraint(equalTo: container.trailingAnchor), view.topAnchor.constraint(equalTo: container.topAnchor), view.bottomAnchor.constraint(equalTo: container.bottomAnchor), // 固定预览宽度,高度自适应 view.widthAnchor.constraint(equalToConstant: 400) ]) container.setContentHuggingPriority(.required, for: .vertical) container.setContentHuggingPriority(.required, for: .horizontal) return container } func updateUIView(_ view: UIView, context: Context) {} } struct MyCell_Previews: PreviewProvider { static var previews: some View { UIViewPreview { MyCell() } .previewLayout(.sizeThatFits) } }
快速预览方案(重写固有尺寸)
如果不需要通用包装器,可以直接在cell中重写intrinsicContentSize,强制返回基于约束计算的适配尺寸:
class MyCell: UICollectionViewCell { // 省略原有初始化代码... override var intrinsicContentSize: CGSize { contentView.layoutIfNeeded() return contentView.systemLayoutSizeFitting( CGSize(width: 400, height: UIView.layoutFittingCompressedSize.height), withHorizontalFittingPriority: .required, verticalFittingPriority: .fittingSizeLevel ) } }
效果对比
- 修复前预览:

- 修复后预览(与预期SwiftUI实现效果一致):

内容的提问来源于stack exchange,提问作者Richard Witherspoon
相关产品推荐
相关产品推荐

