SwiftUI 视图大小在添加到 UITableViewController 时被破坏

问题描述

我需要在现有的 UITableViewController显示一个 SwiftUI 视图,作为 tableHeaderView。但是,当将 SwiftUI 视图添加UITableViewController 时,它的大小似乎被破坏了。

如果我只是使用 UIView 将我的 SwiftUI 视图转换为 UIHostingController 并将其设置为 tableHeaderView,则视图会显示在屏幕外:

func addHeaderView() {
    let view = VerticalTextStack()
    let hostingController = UIHostingController(rootView: view)
    tableView.tableHeaderView = hostingController.view
}

Incorrect layout

为了解决这个问题,我尝试了几种不同的方式来修复视图的高度。添加 NSLayoutConstraint 没有任何作用。手动设置 tableHeaderView.frame.size 时,结果更好,因为至少现在视图显示在屏幕上,但多行 Text 变为单行并被截断。

正如你在这里看到的,第二个 Text 被截断了:

Truncated Text

这里有一个简单的例子来说明问题:

/// `UITableViewController` displaying a `UIView` as its `tableHeaderView`
class TableViewController: UITableViewController {
    let cellReuseIdentifier = "cell"
    let data = ["A","B","C","D","E"]
    let themeManager = AppThemeManager()

    // MARK: - UIViewController lifecycle
    override func viewDidLoad() {
        super.viewDidLoad()

        tableView.register(UITableViewCell.self,forCellReuseIdentifier: cellReuseIdentifier)
        addHeaderView()
    }

    override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()

        fixTableHeaderViewSize()
    }

    // MARK: - UITableViewDelegate
    override func tableView(_ tableView: UITableView,numberOfRowsInSection section: Int) -> Int {
        data.count
    }

    override func tableView(_ tableView: UITableView,cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier,for: indexPath)
        cell.textLabel?.text = data[indexPath.row]
        return cell
    }

    // MARK: - SwiftUI view
    func fixTableHeaderViewSize() {
        guard let tableHeaderView = tableView?.tableHeaderView else { return }
        let expectedHeight = tableHeaderView.systemLayoutSizefitting(UIView.layoutFittingExpandedSize).height
        let expectedSize = CGSize(width: tableHeaderView.frame.width,height: expectedHeight)

        tableHeaderView.frame.size = expectedSize
    }

    func addHeaderView() {
        let view = VerticalTextStack()
        let hostingController = UIHostingController(rootView: view)
        tableView.tableHeaderView = hostingController.view
    }
}

private struct VerticalTextStack: View {
    let data = ["First","I am a very long text that only fits in multiple lines. I still continue.","Third"]
    let themeManager = AppThemeManager()

    var body: some View {
        vstack(spacing: 10) {
            ForEach(data,id: \.self) { value in
                Text(value)
            }
        }
    }
}

我还尝试将 addHeaderView 移至其他 UIViewController 函数,例如 viewWillLayoutSubviews,但这没有任何改变。

lineLimit 设置为 nilText 内的 VerticalTextStack 上的任何大数并将 .layoutPriority(.greatestFiniteMagnitude) 添加Text 都不会使Text 也可以是多行。

解决方法

这是一个可能的解决方案。

使用此更改添加标题视图功能。

func addHeaderView() {
    
    let view = VerticalTextStack()
    let hostingController = UIHostingController(rootView: view)
    
    let headerViewMain = UIView()
    headerViewMain.backgroundColor = .red
    headerViewMain.addSubview(hostingController.view)
    
    hostingController.view.translatesAutoresizingMaskIntoConstraints = false
    
    let constraints = [
        hostingController.view.topAnchor.constraint(equalTo: headerViewMain.topAnchor),hostingController.view.leftAnchor.constraint(equalTo: headerViewMain.leftAnchor),headerViewMain.bottomAnchor.constraint(equalTo: hostingController.view.bottomAnchor),headerViewMain.rightAnchor.constraint(equalTo: hostingController.view.rightAnchor)
    ]
    
    NSLayoutConstraint.activate(constraints)
    
    headerViewMain.frame.size.height = headerViewMain.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).height
    headerViewMain.frame.size.width = headerViewMain.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).width
    self.tableHeaderView = headerViewMain
    
    self.layoutIfNeeded()
    self.setNeedsLayout()
    
    self.reloadData()
}