【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 根据父视图的高度正确调整。

相关推荐
wow_DG1 天前
【Python✨】VS Code 秒开 Python 类型检查:一招 mypy + settings.json 让你的 Bug 原地现形!
python·json·bug
RickeyBoy2 天前
SwiftUI 手势冲突:修复 Navigation 返回手势
swiftui
许泽宇的技术分享2 天前
用 OpenAI Whisper + pyannote.audio 打造“谁在说什么”的全栈语音理解系统
macos·whisper·xcode
xiAo_Ju2 天前
SwiftUI快速入门指南-关键字篇
swiftui
xiAo_Ju2 天前
SwiftUI快速入门指南-Modifier篇
swiftui
东坡肘子3 天前
毕业 30 年同学群:一场 AI 引发的“真假难辨”危机 -- 肘子的 Swift 周报 #112
人工智能·swiftui·swift
2501_941798733 天前
C++高性能音频处理与实时特征提取实战分享:多线程信号处理与低延迟优化经验
ide·macos·xcode
Morgan-Chen3 天前
iOS开发针对苹果新系统iOS26的兼容适配
ios·objective-c·xcode·ios26
驱动探索者4 天前
Zephyr 获取 cpu 占用率异常bug分析
bug·rtos·zephyr
薛定e的猫咪4 天前
【调试技巧】vscode 四种断点调试,快速定位 bug
ide·vscode·python·bug