我有一个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"
}
}
字符串
故事板文件基本上是初始项目,除了我将VC嵌入到了一个nav vc下。
的数据
在上面的代码中,ListView
是使用已弃用的NavigationView
实现的,它与UIHostingController
配合良好。ListViewWithNewAPI
是使用新的NavigationStack
API的新实现,我无法复制原始行为。
这里有一个比较两种行为的视频。请使用示例代码并进行演示,看看我们是否可以使用新的API实现原始行为。
的
2条答案
按热度按时间7gyucuyw1#
NavigationStack取代了NavigationView。在你的代码中,你将UIHostingController压入到navigationController上。然而,ListViewWithNewAPI中的视图包含了一个NavigationStack。这意味着你现在有两个级别的“导航堆栈”,导致了不期望的行为。
以下是解决此问题的三个不同选项:
1.一种选择是在故事板中删除Navigation Controller。您不能在故事板中直接使用UIHostingController,因为它使用泛型,但您可以将其 Package 在(非UINavigationController)UIViewController中。或者您可以完全删除故事板并采用SwiftUI应用生命周期。然后您可以按原样使用ListViewWithNewAPI中的代码。
1.您可以继续使用NavigationLink(destination:label:),它没有被弃用,并继续与UINavigationController结合使用。不幸的是,这意味着没有编程或基于值的导航。
字符串
1.如果你需要保留UINavigationController(因为其他基于UIKit的代码),并且你想要编程/基于值的导航,你可以做以下事情:
SwiftUI中使用的导航模型
型
将navigationController添加到模型中,并将模型提供给视图
型
这就是你如何使用导航模型
型
wvyml7n52#
我还遇到了这个双导航栏问题(UIKit UINavigationController推送一个UIHostingController,其rootView嵌入了
NavigationStack
)。对于我的用例,我真的想保持编程.navigationDestination(<type>) {…}
NavigationStack
导航,主要是因为我需要保持目标视图“懒惰”(所以旧的NavigationLink(<destination>, <label>)
方法不会“懒惰”,除非我进行了重大的黑客攻击)。我还需要为应用程序的其他非SwiftUI部分保留UIKit navigationController。所以我的解决方案是在SwiftUI视图中隐藏UIKit导航栏,然后在NavigationStack中模拟返回按钮。动画等不是无缝的,但它符合我的需求。
字符串
我还需要一个自定义的UIHostingController,以便在从SwiftUI视图转换回UIKit视图时重新显示UIKit导航栏。
型