使用SwiftUI Hosting Cell时滚动出现单元格重叠问题求助
问题描述
我尝试通过Hosting Cell将SwiftUI视图作为UITableView的单元格视图,已设置UITableViewAutomaticDimension为单元格高度。但滚动时出现单元格重叠的情况,我猜测这可能与单元格复用有关,请问该如何解决这个问题?
相关代码
单元格获取方法
private func cellView(_ index: Int) -> UITableViewCell { guard let filterCell = tableView.dequeueReusableCell(withIdentifier: "HostingCell<CellView>") as? HostingCell<CellView>, let viewModel = viewModel.data else { return HostingCell<CellView>() } let cellViewModel = viewModel.viewModelForRadioButton(at: index, theme: theme) filterCell.set(rootView: FilterCellView(viewModel: cellViewModel, isSelected: viewModel.selectedIndex() == index), parentController: self) return filterCell }
HostingCell实现
class HostingCell <Content: View>: UITableViewCell { private let hostingController = UIHostingController<Content?>(rootView: nil) override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } func set(rootView: Content, parentController: UIViewController, hostingControllerBackground: UIColor? = nil) { self.hostingController.rootView = rootView self.hostingController.view.backgroundColor = hostingControllerBackground self.hostingController.view.invalidateIntrinsicContentSize() let requiresControllerMove = hostingController.parent != parentController if requiresControllerMove { parentController.addChild(hostingController) } if !self.contentView.subviews.contains(hostingController.view) { self.contentView.addSubview(hostingController.view) hostingController.view.translatesAutoresizingMaskIntoConstraints = false hostingController.view.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor).isActive = true hostingController.view.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor).isActive = true hostingController.view.topAnchor.constraint(equalTo: self.contentView.topAnchor).isActive = true hostingController.view.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor).isActive = true } if requiresControllerMove { hostingController.didMove(toParent: parentController) } }
问题截图

解决方案
你的猜测正确,单元格重叠确实是复用机制下内容清理不彻底、布局刷新不及时导致的,以下是具体修复步骤:
1. 重写prepareForReuse清理旧内容
在HostingCell中添加该方法,重置hostingController的内容,避免复用旧的SwiftUI视图:
override func prepareForReuse() { super.prepareForReuse() hostingController.rootView = nil hostingController.view.invalidateIntrinsicContentSize() contentView.layoutIfNeeded() }
2. 优化set方法的布局刷新逻辑
设置新的rootView后,强制触发多层布局更新,确保SwiftUI视图的高度被正确计算:
func set(rootView: Content, parentController: UIViewController, hostingControllerBackground: UIColor? = nil) { self.hostingController.rootView = rootView self.hostingController.view.backgroundColor = hostingControllerBackground // 先更新内容,再逐层刷新布局 hostingController.view.invalidateIntrinsicContentSize() hostingController.view.layoutIfNeeded() contentView.layoutIfNeeded() let requiresControllerMove = hostingController.parent != parentController if requiresControllerMove { parentController.addChild(hostingController) } if !self.contentView.subviews.contains(hostingController.view) { self.contentView.addSubview(hostingController.view) hostingController.view.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ hostingController.view.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), hostingController.view.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), hostingController.view.topAnchor.constraint(equalTo: contentView.topAnchor), hostingController.view.bottomAnchor.constraint(equalTo: contentView.bottomAnchor) ]) } if requiresControllerMove { hostingController.didMove(toParent: parentController) } }
3. 修正单元格复用的初始化问题
当前代码中,dequeueReusableCell失败时直接初始化的单元格未设置复用标识符,会导致后续复用异常,修改为:
private func cellView(_ index: Int) -> UITableViewCell { let cellIdentifier = "HostingCell<CellView>" guard let viewModel = viewModel.data else { return UITableViewCell(style: .default, reuseIdentifier: cellIdentifier) } let filterCell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) as? HostingCell<CellView> ?? HostingCell<CellView>(style: .default, reuseIdentifier: cellIdentifier) let cellViewModel = viewModel.viewModelForRadioButton(at: index, theme: theme) filterCell.set(rootView: FilterCellView(viewModel: cellViewModel, isSelected: viewModel.selectedIndex() == index), parentController: self) return filterCell }
4. 完善TableView的高度配置
除了设置自动高度,还需指定合理的预估高度,帮助TableView减少滚动时的布局跳动:
// 在viewDidLoad或TableView初始化时设置 tableView.rowHeight = UITableViewAutomaticDimension tableView.estimatedRowHeight = 80 // 根据你的Cell内容设置一个接近的预估高度
内容的提问来源于stack exchange,提问作者Pratyush Kumar
相关产品推荐
相关产品推荐

