该示例在Xcode 14.0+中编译,如果在模拟器或具有iOS 16.0的设备上运行,则会产生我所描述的bug。我想知道这是缺乏知识还是平台缺陷。通过我的日志,我可以看到,当我用不完整的滑动手势诱发bug时,导航路径的元素计数将上升到2,而实际上应在根处返回到0,并且仅在第一层视图中保留1个元素。
是否有一种方法可以更好地管理此类视图层次结构的路径?或者,这是平台级别的bug吗?
import SwiftUI
enum AppViews: Hashable {
case kombuchaProductsView
case coffeeProductsView
case customerCartView
}
struct RootView: View {
@StateObject var drinkProductViewModel = DrinkProductViewModel()
var body: some View {
NavigationStack(path: self.$drinkProductViewModel.navPath) {
List {
Section("Products") {
NavigationLink(value: AppViews.kombuchaProductsView) {
HStack {
Text("View all Kombuchas")
Spacer()
Image(systemName: "list.bullet")
}
}
NavigationLink(value: AppViews.coffeeProductsView) {
HStack {
Text("View all Coffees")
Spacer()
Image(systemName: "list.bullet")
}
}
}
Section("Checkout") {
NavigationLink(value: AppViews.customerCartView) {
HStack {
Text("Cart")
Spacer()
Image(systemName: "cart")
}
}
}
}
.navigationDestination(for: AppViews.self) { appView in
switch appView {
case .kombuchaProductsView:
KombuchaProductsView(drinkProductViewModel: self.drinkProductViewModel)
case .coffeeProductsView:
CoffeeProductsView(drinkProductViewModel: self.drinkProductViewModel)
case .customerCartView:
Text("Not implemented")
}
}
}
.onAppear {
print("RootView appeared.")
print("Nav stack count: \(self.drinkProductViewModel.navPath.count) (RootView)")
}
}
}
struct KombuchaProductsView: View {
@ObservedObject var drinkProductViewModel: DrinkProductViewModel
var body: some View {
ScrollView {
VStack(spacing: 16) {
ForEach(drinkProductViewModel.kombuchaProducts, id: \.self) { kombucha in
NavigationLink {
KombuchaView(
drinkProductViewModel: self.drinkProductViewModel,
kombucha: kombucha
)
} label: {
HStack {
Text(kombucha.name)
Spacer()
Text("$\(kombucha.price)")
Image(systemName: "chevron.right")
.foregroundColor(.gray)
}
}
Divider()
}
.padding()
}
}
.navigationTitle("Kombucha Selection")
.onAppear {
print("KombuchaProductsView appeared.")
print("Nav stack count: \(self.drinkProductViewModel.navPath.count) (KombuchaProductsView)")
}
.onDisappear {
print("KombuchaProductsView disappeared")
}
}
}
struct CoffeeProductsView: View {
@ObservedObject var drinkProductViewModel: DrinkProductViewModel
var body: some View {
ScrollView {
VStack(spacing: 16) {
ForEach(drinkProductViewModel.coffeeProducts, id: \.self) { coffee in
NavigationLink {
CoffeeView(
drinkProductViewModel: self.drinkProductViewModel,
coffee: coffee
)
} label : {
HStack {
Text(coffee.name)
Spacer()
Text("$\(coffee.price)")
Image(systemName: "chevron.right")
.foregroundColor(.gray)
}
}
Divider()
}
.padding()
}
}
.navigationTitle("Coffee Selection")
.onAppear {
print("CoffeeProductsView appeared")
print("Nav stack count: \(self.drinkProductViewModel.navPath.count) (CoffeeProductsView)")
}
.onDisappear {
print("CoffeeProductsView disappeared")
}
}
}
struct KombuchaView: View {
@ObservedObject var drinkProductViewModel: DrinkProductViewModel
@State var kombucha: Kombucha
var body: some View {
VStack {
Text("Price:")
.font(.title)
Text("\(kombucha.price)")
.font(.callout)
}
.navigationTitle(kombucha.name)
.onAppear {
print("Nav stack count: \(self.drinkProductViewModel.navPath.count) (KombuchaView)")
}
}
}
struct CoffeeView: View {
@ObservedObject var drinkProductViewModel: DrinkProductViewModel
@State var coffee: Coffee
var body: some View {
VStack {
Text("Price:")
.font(.title)
Text("\(coffee.price)")
.font(.callout)
}
.navigationTitle(coffee.name)
.onAppear {
print("Nav stack count: \(self.drinkProductViewModel.navPath.count) (CoffeeView)")
}
}
}
对于那些对编译我的示例感兴趣的人,以下是我的虚拟ViewModel(它只包含静态数据 - 纯粹为了进行探索而构建):
class DrinkProductViewModel: ObservableObject {
@Published var navPath = NavigationPath()
@Published var customerCart = [Any]()
@Published var kombuchaProducts = [Kombucha]()
@Published var coffeeProducts = [Coffee]()
init() {
// Let's ignore networking, and assume a bunch of static data
self.kombuchaProducts = [
Kombucha(name: "Ginger Blast", price: 4.99),
Kombucha(name: "Cayenne Fusion", price: 6.99),
Kombucha(name: "Mango Tango", price: 4.49),
Kombucha(name: "Clear Mind", price: 5.39),
Kombucha(name: "Kiwi Melon", price: 6.99),
Kombucha(name: "Super Berry", price: 5.99)
]
self.coffeeProducts = [
Coffee(name: "Cold Brew", price: 2.99),
Coffee(name: "Nitro Brew", price: 4.99),
Coffee(name: "Americano", price: 6.99),
Coffee(name: "Flat White", price: 5.99),
Coffee(name: "Espresso", price: 3.99)
]
}
func addToCustomerCart() {
}
func removeFromCustomerCart() {
}
}
请注意:通过不完整的划动手势,我指的是用户从前沿开始拖动屏幕,然后将其保持并放回到起始位置,并释放它,以便用户通过不返回而保持在当前视图中。 然后返回到父视图(而非根视图)将导致导航链接失效。
你可以观察我所描述的错误,方法是未能完成从kombucha或coffee详细视图(最深层子视图)滑动返回手势,然后返回到其中一个产品列表视图,并尝试单击其中一个导航链接(应该无法点击)。
通常情况下,返回到根视图会在运行时清除此场景并恢复NavigationLink功能。
NavigationStack
,则导航路径必须有一个单一的真实来源,并由导航引擎自动管理。此外,在应用程序的根级别仅使用一个NavigationStack
是建议的。很好,您没有使用已弃用的NavigationLinks
。使用基于值的链接将确保您遵守新API的设计。据我所知,模型还必须符合Identifiable
以防止出现问题。我可能会在下周有时间继续调试并希望发布解决方案。 - Andre