问题描述
在 SwiftUI 中嵌套使用 UIKit 的 UITableView
时,你可能会遇到一个常见的 Bug:UITableView
的高度没有正确设置,导致内容无法正常滚动,尤其是滚动到页面底部时。
核心问题 在于 SwiftUI 和 UIKit 的布局机制不同。SwiftUI 使用声明式布局系统,而 UIKit 使用传统的视图层次结构。如果没有明确告诉 UITableView
应该占用多少高度,UIKit 中的 UITableView
可能会默认按照父视图的尺寸计算,忽略 SwiftUI 布局中的其他组件。因此,UITableView
的高度和滚动行为就会出现问题。
解决思路
为了让 UITableView
在 SwiftUI 布局中能够正确滚动到底,我们需要:
- 动态获取 SwiftUI 中的可用高度 ,通过 SwiftUI 的
GeometryReader
计算当前布局的高度。 - 将高度传递给 UIKit 中的
UITableView
,确保它根据父视图的高度动态调整。
以下是一个解决该问题的demo示例
1. 使用 UIViewControllerRepresentable
桥接 SwiftUI 和 UIKit
我们首先需要创建一个桥接类,让 SwiftUI 能够嵌套 UIKit 组件。在这个桥接类中,我们接收从 SwiftUI 获取的高度,然后在 UIKit 组件中使用这个高度动态调整 UITableView
的大小。
swift
import SwiftUI
struct MyTableViewWrapper: UIViewControllerRepresentable {
var height: CGFloat // 接收传递过来的高度
func makeUIViewController(context: Context) -> MyTableViewController {
let controller = MyTableViewController()
controller.height = height // 设置高度
return controller
}
func updateUIViewController(_ uiViewController: MyTableViewController, context: Context) {
uiViewController.height = height // 动态更新高度
}
}
MyTableViewWrapper
作为 SwiftUI 和 UIKit 的桥接类,它允许我们将 SwiftUI 的布局数据(比如高度)传递给 UIKit 中的 UITableView
。
2. 在 UIViewController
中调整 UITableView
的高度
在自定义的 UIViewController
中,我们使用传入的高度来设置 UITableView
的 frame,这样可以确保 UITableView
的大小根据 SwiftUI 的布局进行动态调整。
swift
import UIKit
class MyTableViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var height: CGFloat = 0 // 用于接收传进来的高度
override func viewDidLoad() {
super.viewDidLoad()
// 创建 UITableView,并根据传入的高度调整它的大小
let tableView = UITableView(frame: CGRect(x: 0, y: 0, width: view.bounds.width, height: height), style: .plain)
tableView.dataSource = self
tableView.delegate = self
view.addSubview(tableView)
}
// UITableViewDataSource 协议实现
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 20
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .default, reuseIdentifier: "cell")
cell.textLabel?.text = "Row \(indexPath.row)"
return cell
}
}
在这个自定义的 UIViewController
中,UITableView
的高度由外部传入并动态调整,这样就避免了 UITableView
默认假设自己占据全屏的问题。
3. 使用 GeometryReader
获取 SwiftUI 中的高度
接下来,我们需要在 SwiftUI 布局中使用 GeometryReader
来动态获取可用的高度,并将其传递给 UIKit 的 UITableView
。
swift
struct ContentView: View {
var body: some View {
GeometryReader { geometry in
VStack {
Text("这里是一些顶部内容")
.padding()
// 将当前视图的高度传递给 UIKit 中的 TableView
MyTableViewWrapper(height: geometry.size.height * 0.6) // 比如占屏幕60%高度
Spacer()
}
.padding()
}
}
}
在这个布局中,GeometryReader
会动态计算当前视图的高度,并将这个高度传递给 MyTableViewWrapper
,从而确保 UITableView
根据父视图的高度正确调整。