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

相关推荐
humiaor13 小时前
Xcode报错:“Set `maskView` to `nil` before adding it as a subview of ZFMaskView
ide·macos·xcode·ios18报错
@Aurora.20 小时前
【测试】BUG
bug
运维-大白同学1 天前
go-中间件的使用
中间件·golang·xcode
%d%d23 天前
undefined reference to CPUAllocatorSingleton::instance
bug
gxn_mmf5 天前
典籍知识问答重新生成和消息修改Bug修改
前端·bug
千里马学框架6 天前
重学安卓14/15自由窗口freeform企业实战bug-学员作业
android·framework·bug·systrace·安卓framework开发·安卓窗口系统·自由窗口
Bear on Toilet7 天前
Bug日记——实现“日期类”
开发语言·c++·bug
Direction_Wind7 天前
flinksql bug : Max aggregate function does not support type: CHAR
bug
mg6687 天前
Python Bug 修复案例分析:多线程数据竞争引发的bug 两种修复方法
bug
会游泳的石头7 天前
在Java项目中实现本地语音识别与热点检测,并集成阿里云智能语音服务(优化版)
阿里云·语音识别·xcode