SwiftUI UIHostingController 导航标题动画坏了
SwiftUI UIHostingController navigation title animation broken
当我使用 UIHostingController 将新 SwiftUI.View 推送到现有 UIKit UIViewController 的导航堆栈时,导航栏中的标题动画被破坏。我在 Xcode 12.0 中测试了一个纯新项目。
仔细看标题“UIHostingController”。你可以看到动画看起来与普通的推送动画有何不同,它只是无中生有地“出现”并且看起来很破烂。第二个动画已经发生在 SwiftUI.NavigationLink 看起来不错。
如果您想尝试一下,这里有一个 link 示例项目:
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")
}
}
已找到此问题的解决方案。 UIHostingController
实际上只是一个普通的 UIViewController
(带有 SwiftUI 的一些附加组件)。这意味着可用于 UIViewController
的所有内容也可用于 UIHostingController
。因此,解决方案是在 UIHostingController
上设置与导航栏相关的所有内容,而不是在包装的 SwiftUI 视图中。
let vc = UIHostingController(rootView: Content())
vc.title = "My custom title"
如果直接在 UIHostingController
上设置,所有导航按钮也能更好地工作。一个很好的选择也是直接从 UIHostingController
派生并在那里实现自定义所需的行为。
要强制 UIHostingController
设置它的导航项,我们可以 pre-render 它在一个单独的 window:
let window = UIWindow(frame: .zero)
window.rootViewController = UINavigationController(rootViewController: hostingController)
window.isHidden = false
window.layoutIfNeeded()
window 甚至可以缓存 pre-rendering 其他视图。
当我使用 UIHostingController 将新 SwiftUI.View 推送到现有 UIKit UIViewController 的导航堆栈时,导航栏中的标题动画被破坏。我在 Xcode 12.0 中测试了一个纯新项目。
仔细看标题“UIHostingController”。你可以看到动画看起来与普通的推送动画有何不同,它只是无中生有地“出现”并且看起来很破烂。第二个动画已经发生在 SwiftUI.NavigationLink 看起来不错。
如果您想尝试一下,这里有一个 link 示例项目: 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")
}
}
已找到此问题的解决方案。 UIHostingController
实际上只是一个普通的 UIViewController
(带有 SwiftUI 的一些附加组件)。这意味着可用于 UIViewController
的所有内容也可用于 UIHostingController
。因此,解决方案是在 UIHostingController
上设置与导航栏相关的所有内容,而不是在包装的 SwiftUI 视图中。
let vc = UIHostingController(rootView: Content())
vc.title = "My custom title"
如果直接在 UIHostingController
上设置,所有导航按钮也能更好地工作。一个很好的选择也是直接从 UIHostingController
派生并在那里实现自定义所需的行为。
要强制 UIHostingController
设置它的导航项,我们可以 pre-render 它在一个单独的 window:
let window = UIWindow(frame: .zero)
window.rootViewController = UINavigationController(rootViewController: hostingController)
window.isHidden = false
window.layoutIfNeeded()
window 甚至可以缓存 pre-rendering 其他视图。