SwiftUI UIHostingController导航标题动画失效。

7

当我使用UIHostingController将一个新的SwiftUI.View推入到现有的UIKit UIViewController导航堆栈中时,导航栏标题的动画会出现问题。我在Xcode 12.0上测试了一个全新项目。

仔细观察"UIHostingController"这个标题。您可以看到它与普通推送动画不同,它只是从无到有地“出现”,看起来有问题。第二个动画已经从SwiftUI.NavigationLink发生,它看起来很好。

如果您想尝试一下,请点击以下链接下载示例项目: https://www.dropbox.com/s/mjkuzhpsb6yvlir/HostingControllerTest.zip?dl=0

请查看此GIF图像:(如果未看到GIF动画,请在另一个浏览器选项卡中打开)

以下是代码:

class ViewController: UIViewController {
    private let button = UIButton(frame: .zero)

    override func viewDidLoad() {
        super.viewDidLoad()
        self.title = "UIHostingController Title Test"
        self.view.backgroundColor = UIColor.white
        
        self.view.addSubview(self.button)
        button.translatesAutoresizingMaskIntoConstraints = false
        button.setTitle("Push UIHostingController", for: .normal)
        button.addTarget(self, action: #selector(Self.pushVC), for: .touchUpInside)
        button.setTitleColor(.blue, for: .normal)
        
        NSLayoutConstraint.activate([
            button.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
            button.centerYAnchor.constraint(equalTo: self.view.centerYAnchor),
            button.widthAnchor.constraint(equalTo: self.view.widthAnchor),
            button.heightAnchor.constraint(equalToConstant: 50)
        ])
    }
    
    @objc private func pushVC() {
        let vc = UIHostingController(rootView: Content())
        self.navigationController?.pushViewController(vc, animated: true)
    }
}

struct Content: View {
    var body: some View {
        NavigationLink(destination: Content2()) {
            Text("Push NavigationLink")
        }
        .navigationTitle("UIHostingController")
    }
}

struct Content2: View {
    var body: some View {
        Text("Coming from NavigationLink")
            .navigationTitle("Native SwiftUI View")
    }
}

我认为我有同样的问题,如果你在模拟器中打开Debug/Slow Animations,就会更容易看到。标题(以及返回按钮和栏目项)几乎在最后一刻才“闪现”出来,而不是淡入。你解决了吗? - Robert Atkins
是的,目前找到了最佳解决方案。请看我的答案。 - Darko
2个回答

10

我找到了解决这个问题的方法。UIHostingController实际上只是一个普通的UIViewController(带有一些SwiftUI的附加功能)。这意味着所有可用于UIViewController的东西也同样适用于UIHostingController。因此,解决方法是将与导航栏相关的所有内容设置为UIHostingController,而不是在封装的SwiftUI视图中设置。

let vc = UIHostingController(rootView: Content())
vc.title = "My custom title"

如果直接在 UIHostingController 上设置所有导航按钮,则效果更佳。另一种好的选择是直接从 UIHostingController 派生并在其中实现所需的自定义行为。


2

为了强制UIHostingController设置其导航项,我们可以在单独的窗口中预先呈现它:

let window = UIWindow(frame: .zero)
window.rootViewController = UINavigationController(rootViewController: hostingController)
window.isHidden = false
window.layoutIfNeeded()

窗口甚至可以被缓存以预先呈现其他视图。
更新:此解决方法似乎在iOS 16.4上停止工作。

这个解决方法在处理导航堆栈推送大量“预加载”窗口时,也会导致重复重叠的导航栏出现.... :( 这似乎是16.6版本(可能追溯到16.4)存在的问题。 - spentag
1
@spentag 是的,我们也不得不完全切换到NavigationStack,因为苹果似乎已经放弃支持UIHostingController,导致每个新的iOS版本都引入了更多问题。 - Nickolay Tarbayev
@nikolay- 我们发现这实际上是由于一个属性字符串尝试在主线程上渲染 SwiftUI 视图中的 HTML 而引起的。我们将该属性字符串的 HTML 初始化移出视图,解决了这个错误。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接