iOS15及以下适配:含SwiftUI View的动态高度UITableViewCell实现
iOS15及以下实现UITableViewCell高度随内部SwiftUI View动态更新的方案
针对iOS15及以下版本,要实现UITableViewCell高度随内部SwiftUI View的动态内容变化同步更新,核心是让UITableView感知到SwiftUI View的尺寸变化,重新计算单元格高度。以下是具体实现步骤和代码示例:
1. 让ViewModel支持内容更新回调
在SwiftUI View的ViewModel中添加回调闭包,当API请求完成、数据更新后触发回调,通知外部内容已变更:
class MyCustomSwiftUIViewModel { let productCode: String var onContentUpdated: (() -> Void)? init(productCode: String) { self.productCode = productCode } // 模拟API数据请求 func fetchData() { DispatchQueue.global().asyncAfter(deadline: .now() + 1) { // 数据加载完成后更新内部状态 // ... // 主线程通知外部内容更新 DispatchQueue.main.async { self.onContentUpdated?() } } } }
2. 改造UITableViewCell,监听内容变化并触发更新
将UIHostingController作为Cell的属性持有(避免局部变量被释放),并在ViewModel的回调中触发布局更新和TableView的行重加载:
class CustomTableViewCell: UITableViewCell { private var hostingController: UIHostingController<MyCustomSwiftUIView>? override func prepareForReuse() { super.prepareForReuse() // 复用前清理旧的HostingController,避免内存泄漏 hostingController?.view.removeFromSuperview() hostingController?.willMove(toParent: nil) hostingController?.removeFromParent() hostingController = nil } func configure(with productCode: String) { let viewModel = MyCustomSwiftUIViewModel(productCode: productCode) let swiftUIView = MyCustomSwiftUIView(viewModel: viewModel) let vc = UIHostingController(rootView: swiftUIView) hostingController = vc // 将HostingController的View添加到Cell的contentView并设置约束 vc.view.translatesAutoresizingMaskIntoConstraints = false contentView.addSubview(vc.view) NSLayoutConstraint.activate([ vc.view.topAnchor.constraint(equalTo: contentView.topAnchor), vc.view.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), vc.view.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), vc.view.bottomAnchor.constraint(equalTo: contentView.bottomAnchor) ]) // 将HostingController添加到根控制器,保证生命周期正常 self.window?.rootViewController?.addChild(vc) vc.didMove(toParent: self.window?.rootViewController) // 监听内容更新,同步Cell高度 viewModel.onContentUpdated = { [weak self] in guard let self = self else { return } // 强制布局更新,让SwiftUI View的新尺寸生效 self.contentView.layoutIfNeeded() // 找到当前Cell对应的IndexPath,通知TableView重加载该行 if let tableView = self.superview as? UITableView, let indexPath = tableView.indexPath(for: self) { tableView.reloadRows(at: [indexPath], with: .automatic) } } // 触发API请求 viewModel.fetchData() } }
3. 配置UITableView支持自动高度
确保TableView开启自动高度计算,并设置合理的预估高度:
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { private let tableView = UITableView() override func viewDidLoad() { super.viewDidLoad() setupTableView() } private func setupTableView() { tableView.translatesAutoresizingMaskIntoConstraints = false view.addSubview(tableView) NSLayoutConstraint.activate([ tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor), tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor), tableView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor) ]) tableView.register(CustomTableViewCell.self, forCellReuseIdentifier: "CustomCell") // 开启自动高度计算 tableView.rowHeight = UITableView.automaticDimension // 设置预估高度,优化滚动体验 tableView.estimatedRowHeight = 200 tableView.dataSource = self tableView.delegate = self } // UITableView数据源实现 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 10 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomTableViewCell cell.configure(with: "\(indexPath.row)") return cell } }
关键注意事项
- 内存泄漏防护:在ViewModel的回调中使用
[weak self],避免Cell与ViewModel形成循环引用;复用Cell时务必清理旧的HostingController。 - 布局强制更新:调用
contentView.layoutIfNeeded()确保SwiftUI View的新尺寸被AutoLayout系统识别,为TableView计算正确高度提供依据。 - 预估高度设置:必须设置
estimatedRowHeight,否则UITableView无法正确触发自动高度计算,可能导致高度异常或滚动卡顿。
内容的提问来源于stack exchange,提问作者pravir
相关产品推荐
相关产品推荐

