I need to use uitableview to draw a message list.
In each tableviewcell, I use UIHostingController to load swiftui view. I found that when the page scrolls (and the cell is reused), there will be a werid offset. I used tableView.contentInsetAdjustmentBehavior = .scrollableAxes or automatic and it returned to normal, but when I tried to add any uiview under this tableview, the problem occurred again.
a simple demo code:
import SwiftUI
import Combine
struct DemoView: View {
let height : CGFloat
var body: some View {
ZStack(alignment: .top, content: {
Color.random
Text("RowHeight:\(height)")
ZStack(alignment: .bottom) {
Color.clear
Text("Bottom")
}
})
.frame(height: height)
}
}
class DemoTableView : UIViewController, UITableViewDelegate, UITableViewDataSource {
let ROW_COUNT = 20
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
ROW_COUNT
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return CGFloat(50 + indexPath.row*5)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "cell") {
cell.contentView.subviews.forEach{ $0.removeFromSuperview() }
let ht = self.tableView(tableView, heightForRowAt: indexPath)
let view = DemoView(height: ht)
let host = UIHostingController(rootView: view)
cell.contentView.place(host.view) { v, ly in
ly.inset = .zero
}
self.addChild(host)
host.didMove(toParent: self)
host.view.invalidateIntrinsicContentSize()
return cell
}
return .init(style: .default, reuseIdentifier: "cell")
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.view.backgroundColor = .white
//HERE: I tried to add any uiview under this tableview, then the problem occurred again
self.view.addSubview(UIView())
let tableView = UITableView(frame: self.view.bounds, style: .plain)
//both autoresize & autolayout has the same question
// self.view.addSubview(tableView)
// tableView.frame = self.view.bounds
// tableView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
tableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView)
NSLayoutConstraint.activate([
// tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
// tableView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor),
// tableView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor),
// tableView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
tableView.topAnchor.constraint(equalTo: view.topAnchor),
tableView.leftAnchor.constraint(equalTo: view.leftAnchor),
tableView.rightAnchor.constraint(equalTo: view.rightAnchor),
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
tableView.delegate = self
tableView.dataSource = self
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
tableView.separatorStyle = .none
tableView.contentInsetAdjustmentBehavior = .scrollableAxes
}
}
struct DemoPage : UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> some UIViewController {
return DemoTableView()
}
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
}
}
#Preview {
DemoPage().ignoresSafeArea()
}
I would like to ask if this is a bug in iOS's bridging between uikit and swiftui. Currently, my project uses swiftui to draw each cell, but uitableview is needed to accurately control the scrolling state (swiftui is very troublesome to deal with). In addition, I need to support iOS15, so I cannot use UIHostingConfiguration. Is it a reliable and mature solution to use uitableviewcell to nest uihostingcontroller?