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

Posted

技术标签:

【中文标题】添加到 UITableViewController 时,SwiftUI 视图大小被破坏【英文标题】:SwiftUI view sizing is broken when added to UITableViewController 【发布时间】:2021-04-14 11:54:44 【问题描述】:

我需要在现有的UITableViewController 中显示一个 SwiftUI 视图,如tableHeaderView。但是,似乎 SwiftUI 视图的大小在添加到 UITableViewController 时被破坏了。

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

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

Incorrect layout

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

正如您在此处看到的,第二个 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 设置为nilVerticalTextStack 内部Text 上的任何大数字并将.layoutPriority(.greatestFiniteMagnitude) 添加到Text 也不会使Text 成为多行。

【问题讨论】:

【参考方案1】:

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

用这个改变你的添加标题视图功能。

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()

【讨论】:

这段代码甚至无法编译,因为您似乎认为这是在UITableView 子类而不是UITableViewController 子类中。但是,即使我解决了这些问题,多行文本仍然是单行并被截断。你甚至测试过你提出的解决方案吗?顺便说一句,调用setNeedsLayout 之后 layoutIfNeeded 是一个可怕的想法,你为什么要在设置tableHeaderView 之后调用reloadData 第一个,我忘了这是 UITableViewController 类。是的,我测试了代码,它会在多行中产生问题,我认为在 layoutIfNeeded 之后调用 setNeedsLayout 并不可怕。并且对于重新加载,不调用也没关系。我使用了另一个现有的演示,所以为什么要重新加载代码。 和这一行解决的多行问题Text(value).frame(width: UIScreen.main.bounds.width).fixedSize().lineLimit(nil) 好吧,如果您的代码仍然存在多行文本问题,那么这完全可以解决我的问题吗?多行文本问题是我唯一无法解决的问题,所以这并不比我的原始代码好。调用setNeedsLayout before layoutIfNeeded 可能有意义,但调用它之后根本没有意义。您首先告诉 UIKit 在必要时更新 UI,然后才告诉它有必要更新? @DávidPásztor 检查我的第二条评论是否有多行文本问题。

以上是关于添加到 UITableViewController 时,SwiftUI 视图大小被破坏的主要内容,如果未能解决你的问题,请参考以下文章

手动将 UITabBar 添加到 UITableViewController

从 UITableViewController 添加标记到 UIViewController

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

将 UITableViewController 添加到其他视图

将 UINavigationBar 添加到没有 UINavigationController 的 UITableViewController

将 TAB BAR 添加到 uitableviewcontroller [关闭]