【ios】SwiftUI 混用 UIKit 的 Bug 解决:UITableView 无法滚动到底部

问题描述

在 SwiftUI 中嵌套使用 UIKit 的 UITableView 时,你可能会遇到一个常见的 Bug:UITableView 的高度没有正确设置,导致内容无法正常滚动,尤其是滚动到页面底部时。

核心问题 在于 SwiftUI 和 UIKit 的布局机制不同。SwiftUI 使用声明式布局系统,而 UIKit 使用传统的视图层次结构。如果没有明确告诉 UITableView 应该占用多少高度,UIKit 中的 UITableView 可能会默认按照父视图的尺寸计算,忽略 SwiftUI 布局中的其他组件。因此,UITableView 的高度和滚动行为就会出现问题。

解决思路

为了让 UITableView 在 SwiftUI 布局中能够正确滚动到底,我们需要:

  1. 动态获取 SwiftUI 中的可用高度 ,通过 SwiftUI 的 GeometryReader 计算当前布局的高度。
  2. 将高度传递给 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 根据父视图的高度正确调整。

相关推荐
m0_7482398327 分钟前
前端bug调试
前端·bug
沙尘暴炒饭1 天前
关于uni-forms组件的bug【提交的字段[‘*‘]在数据库中并不存在】
bug
Kisorge2 天前
【C语言】代码BUG排查方式
c语言·开发语言·bug
安卓机器2 天前
探索 Python编程 调试案例:配置日志记录器查看程序运行bug
bug
东坡肘子2 天前
肘子的 Swift 周报 #063|异种肾脏移植取得突破
swiftui·swift·apple
恋猫de小郭3 天前
什么?Flutter 可能会被 SwiftUI/ArkUI 化?全新的 Flutter Roadmap
flutter·ios·swiftui
靴子学长4 天前
iOS + watchOS Tourism App(含源码可简单复现)
mysql·ios·swiftui
一如初夏丿4 天前
xcode15 报错 does not contain ‘libarclite‘
ios·xcode
app开发工程师V帅4 天前
Xcode 文件缺失:Missing submodule xxx
ide·macos·xcode
app开发工程师V帅4 天前
Xcode 16 编译弹窗问题、编译通过无法,编译通过打包等问题汇总
macos·xcode