我有一个 UIKit 应用程序,我使用 UIHostingController 将一些屏幕迁移到 SwiftUI。我曾经能够重复使用 UIKit 的相同导航栏。但在切换到 NavigationStack API 后,我无法复制相同的行为。
这是完整的可重现代码。
import UIKit
import SwiftUI
struct ListView: View {
@State var selectedString: String? = nil
var body: some View {
let details = ["foo", "bar", "baz"]
// No need to wrap under NavigationView, otherwise will have double Nav Bar
// This will use the UIKit's nav bar
List {
ForEach(details, id: \.self) { detail in
let destination = Text("This is a detailed page for \(detail)")
.navigationTitle("Detail page")
NavigationLink(
detail,
destination: destination,
tag: detail,
selection: $selectedString)
}
}
.navigationTitle("List page")
}
}
struct ListViewWithNewAPI: View {
@State var selectedString: String? = nil
var body: some View {
let details = ["foo", "bar", "baz"]
NavigationStack {
List(details, id: \.self, selection: $selectedString) { detail in
NavigationLink(detail, value: detail)
}
.navigationDestination(item: $selectedString) { detail in
Text("This is a detailed page for \(detail)")
.navigationTitle("Detail page")
}
.navigationTitle("List page")
.navigationBarTitleDisplayMode(.inline)
}
}
}
class ViewController: UIViewController {
@objc
private func tapButton1() {
let listVC = UIHostingController(rootView: ListView())
navigationController?.pushViewController(listVC, animated: true)
}
@objc
private func tapButton2() {
let listVC = UIHostingController(rootView: ListViewWithNewAPI())
navigationController?.pushViewController(listVC, animated: true)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let button1 = UIButton(frame: CGRect(x: 100, y: 100, width: 100, height: 100))
button1.backgroundColor = .green
button1.addTarget(self, action: #selector(tapButton1), for: .touchUpInside)
view.addSubview(button1)
let button2 = UIButton(frame: CGRect(x: 100, y: 300, width: 100, height: 100))
button2.backgroundColor = .red
button2.addTarget(self, action: #selector(tapButton2), for: .touchUpInside)
view.addSubview(button2)
navigationItem.title = "UIKit title"
}
}
Run Code Online (Sandbox Code Playgroud)
故事板文件几乎是初始项目,除了我将 VC 嵌入到 nav vc 下。
在上面的代码中,ListView
是使用已弃用的 实现的NavigationView
,它与UIHostingController
. ListViewWithNewAPI
是使用新 API 的新实现NavigationStack
,我无法复制原始行为。
这是一个比较这两种行为的视频。请使用示例代码并尝试一下,看看我们是否可以使用新的 API 实现原始行为。
NavigationStack 取代了 NavigationView。在您的代码中,您将 UIHostingController 推送到导航控制器上。然而,ListViewWithNewAPI 中的视图包含一个 NavigationStack。这意味着您现在有两个级别的“导航堆栈”,从而导致不良行为。
以下是解决该问题的三种不同选项:
一种选择是删除故事板中的导航控制器。您不能直接在故事板中使用 UIHostingController,因为它使用泛型,但您可以将其包装在(非 UINavigationController)UIViewController 中。或者,您可以完全摆脱故事板并采用 SwiftUI 应用程序生命周期。然后,您可以按原样使用 ListViewWithNewAPI 中的代码。
您可以继续使用 NavigationLink(destination: label:),它没有被弃用,并且可以继续与 UINavigationController 结合使用。不幸的是,这意味着没有程序化或基于价值的导航。
struct ListViewWithNewAPI: View {
@State var selectedString: String? = nil
var body: some View {
let details = ["foo", "bar", "baz"]
List(details, id: \.self, selection: $selectedString) { detail in
NavigationLink {
Text("This is a detailed page for \(detail)")
.navigationTitle("Detail page")
} label: {
Text(detail)
}
}
.navigationTitle("List page")
.navigationBarTitleDisplayMode(.inline)
}
}
Run Code Online (Sandbox Code Playgroud)
在 SwiftUI 中使用的导航模型
class NavigationModel {
var navigationController: UINavigationController?
func push<V: View>(@ViewBuilder _ view: () -> V) {
let vc = UIHostingController(rootView: view())
navigationController?.pushViewController(vc, animated: true)
}
}
Run Code Online (Sandbox Code Playgroud)
将 navigationController 添加到模型并将模型提供给您的视图
// property of ViewController
private let navigationModel = NavigationModel()
@objc
private func tapButton2() {
navigationModel.navigationController = navigationController
let rootView = ListViewNavigation(navigationModel: navigationModel)
let hc = UIHostingController(rootView: rootView)
navigationController?.pushViewController(hc, animated: true)
}
Run Code Online (Sandbox Code Playgroud)
这就是您使用导航模型的方式
struct ListViewNavigation: View {
let navigationModel: NavigationModel
let details = ["foo", "bar", "baz"]
var body: some View {
List(details, id: \.self) { detail in
Button(action: { navigateTo(detail) }) {
Text("Go to \(detail)")
}
}
}
func navigateTo(_ detail: String) {
navigationModel.push {
Text("This is a detail page for \(detail)")
}
}
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
405 次 |
最近记录: |