iOS15下UITableViewCell内嵌SwiftUI视图动态高度计算错误问题
iOS15下UITableViewCell嵌入SwiftUI视图复用时高度异常解决方案
问题描述
在UITableViewCell中嵌入SwiftUI视图的实现,在iOS15之前的系统版本运行正常,但在iOS15上滚动UITableView时,初始可见的cell滑出屏幕后,新复用的cell会被添加额外高度,显示异常。
问题根因
该异常由iOS15的两处系统变更触发:
- UITableView新增
sectionHeaderTopPadding属性,默认值为22,会给所有section顶部新增额外空白 - UIHostingController的视图如果没有被正确关联到视图控制器层级,其内部自动尺寸计算逻辑会在cell复用时出现偏差,和UITableView的自动高度计算逻辑冲突,产生多余高度
修复方案
1. 适配UITableView iOS15 默认配置
在ViewController的viewDidLoad方法中添加以下代码,关闭默认的section顶部留白:
override func viewDidLoad() { super.viewDidLoad() // 原有配置保持不变 tableView?.register(TableViewCell.self, forCellReuseIdentifier: "cell") tableView?.rowHeight = UITableView.automaticDimension tableView?.estimatedRowHeight = 100 tableView?.separatorColor = .white tableView?.dataSource = self // 新增iOS15适配代码 if #available(iOS 15.0, *) { tableView?.sectionHeaderTopPadding = 0 } }
2. 优化TableViewCell中UIHostingController的实现
需要持有UIHostingController实例,正确关联控制器生命周期,同时显式设置布局优先级避免约束冲突:
class TableViewCell: UITableViewCell { var stackView: UIStackView = { let stack = UIStackView(frame: .zero) stack.axis = .vertical return stack }() // 持有HostingController实例,避免被提前释放 private var hosting: UIHostingController<TestView>? override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) setupView() } required init?(coder: NSCoder) { super.init(coder: coder) setupView() } func setupView() { contentView.clipsToBounds = true contentView.addSubview(stackView) stackView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ stackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), stackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), stackView.topAnchor.constraint(equalTo: contentView.topAnchor), stackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor) ]) let hosting = UIHostingController(rootView: TestView()) self.hosting = hosting guard let hostingView = hosting.view else { return } hostingView.translatesAutoresizingMaskIntoConstraints = false // 禁用默认安全区域边距适配 hostingView.insetsLayoutMarginsFromSafeArea = false hostingView.backgroundColor = .red stackView.addArrangedSubview(hostingView) // 显式设置垂直方向的尺寸优先级,避免和UITableView预估高度冲突 hostingView.setContentHuggingPriority(.required, for: .vertical) hostingView.setContentCompressionResistancePriority(.required, for: .vertical) } // cell加入视图层级时,关联HostingController到父控制器 override func didMoveToSuperview() { super.didMoveToSuperview() if let parentVC = self.findViewController(), let hosting = hosting, hosting.parent == nil { parentVC.addChild(hosting) hosting.didMove(toParent: parentVC) } } // cell复用时,解绑HostingController的父子关系 override func prepareForReuse() { super.prepareForReuse() if let hosting = hosting, hosting.parent != nil { hosting.willMove(toParent: nil) hosting.removeFromParent() } } } // 扩展UIView工具方法,查找当前视图所属的视图控制器 extension UIView { func findViewController() -> UIViewController? { var nextResponder = self.next while let responder = nextResponder { if let vc = responder as? UIViewController { return vc } nextResponder = responder.next } return nil } }
3. 可选兜底适配
如果仍偶现高度异常,可以给SwiftUI视图添加忽略安全区域的修饰符,强制固定尺寸:
struct TestView: View { var body: some View { Rectangle() .fill(Color.green) .frame(height: 100) .ignoresSafeArea() } }
内容的提问来源于stack exchange,提问作者umali
相关产品推荐
相关产品推荐

