And*_*dre 9 drag swiftui swiftui-navigationlink ios16 swiftui-navigationstack
我正在下面的示例代码中寻找以下错误的解决方案。我尝试使用 SwiftUI 4 和 iOS 16.0 导航 API 变更集实现导航器模式。
下面的示例将在 Xcode 14.0+ 中编译,如果在模拟器或 iOS 16.0 设备中运行,将会产生我所描述的错误。我想知道这是缺乏知识还是平台错误。通过我的日志,我可以看到,当我用不完整的向后滑动手势引发错误时,导航路径的元素计数上升到 2,而实际上它应该在根处返回到 0,并且仅在第一层保留 1 个元素看法。
有没有办法可以更好地管理此类视图层次结构的路径?或者,这是一个平台级错误吗?
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)")
}
}
}
Run Code Online (Sandbox Code Playgroud)
对于那些有兴趣精确编译我的示例的人,下面是我的模拟 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() {
}
}
Run Code Online (Sandbox Code Playgroud)
请注意:通过不完整的滑动手势,我的意思是用户开始从前缘拖动屏幕,然后按住它,然后将其返回到起始位置并释放它,以便用户保持在当前视图中,而不是继续后退。然后返回到父视图(而不是根视图)将导致导航链接失效。
您可以通过未能从康普茶或咖啡详细视图(最深的子视图)完成向后滑动手势来观察我所描述的错误,然后返回到产品列表视图之一并尝试单击导航之一链接(应该是死的)。
返回根视图通常会在运行时清除此场景并恢复 NavigationLink 功能。
我也有同样的问题。
看来 navigationStack 坏了。
即使在苹果官方示例中,“后半滑动”手势也存在同样的问题:https ://developer.apple.com/documentation/swiftui/bringing_robust_navigation_struction_to_your_swiftui_app
我认为在这样做之后,所谓的“后半滑动”导航路径在内部中断了。
如果您澄清 Apple 支持的一些反馈,我将不胜感激!
似乎在 iOS 16.1 中已修复。
基于 Xcode 14.1 构建,首先安装在 iOS 16.0.3 上,出现了问题。然后更新到 iOS 16.1,测试相同的应用程序(无需重新构建或重新安装),问题消失了。可能是 SwiftUI 的错误
iOS 16.0+(在 iOS 16.1 上测试)
导航堆栈路径的模型(基于价值的基础NavigationLink
):
enum ProductViews: Hashable {
case allKombuchas([Kombucha])
case allCoffees([Coffee])
}
enum DrinkProduct: Hashable {
case kombucha(Kombucha)
case coffee(Coffee)
}
Run Code Online (Sandbox Code Playgroud)
模型(符合是最佳实践,可以避免在s 或视图等中Identifiable
使用的需要。不符合 的模型可能会导致竞争条件或其他问题):\.self
List
ForEach
Identifiable
NavigationStack
struct Kombucha: Hashable, Identifiable {
let id = UUID()
var name: String
var price: Double
}
struct Coffee: Hashable, Identifiable {
let id = UUID()
var name: String
var price: Double
}
Run Code Online (Sandbox Code Playgroud)
根视图(导航路径可以存在于对象中,也可以作为视图中ViewModel
自己的成员存在,这在技术上仍然是 MVVM - 请注意,您还可以为您的 使用自定义类型,例如 的数组,然后推送和弹出值到该自定义类型的路径上):@State
NavigationPath
[MyCustomTypes]
struct ParentView: View {
@StateObject var drinkProductViewModel = DrinkProductViewModel()
var body: some View {
ZStack {
NavigationStack(path: self.$drinkProductViewModel.navPath) {
List {
Section("Products") {
NavigationLink(value: ProductViews.allKombuchas(self.drinkProductViewModel.kombuchaProducts)) {
HStack {
Text("Kombuchas")
Spacer()
Image(systemName: "list.bullet")
}
}
NavigationLink(value: ProductViews.allCoffees(self.drinkProductViewModel.coffeeProducts)) {
HStack {
Text("Coffees")
Spacer()
Image(systemName: "list.bullet")
}
}
}
}
.navigationDestination(for: ProductViews.self) { productView in
switch productView {
case .allKombuchas(_):
KombuchaProductsView(drinkProductViewModel: self.drinkProductViewModel)
case .allCoffees(_):
CoffeeProductsView(drinkProductViewModel: self.drinkProductViewModel)
}
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
子视图(使用基于值的NavigationLink
视图很重要,否则可能会导致新的导航 API 出现竞争条件或其他错误):
struct KombuchaProductsView: View {
@State var drinkProductViewModel: DrinkProductViewModel
var body: some View {
ScrollView {
VStack(spacing: 16) {
ForEach(drinkProductViewModel.kombuchaProducts) { kombucha in
NavigationLink(value: kombucha) {
HStack {
Text(kombucha.name)
Spacer()
Text("$\(kombucha.price)")
Image(systemName: "chevron.right")
.foregroundColor(.gray)
}
}
}
.padding()
}
}
.navigationDestination(for: Kombucha.self) { kombucha in
KombuchaView(
drinkProductViewModel: self.drinkProductViewModel,
kombucha: kombucha
)
}
.navigationTitle("Kombucha Selection")
.onDisappear {
print("KombuchaProductsView disappeared")
print("Nav stack count: \(self.drinkProductViewModel.navPath.count) (KombuchaProductsView)")
}
}
}
struct CoffeeProductsView: View {
@State var drinkProductViewModel: DrinkProductViewModel
var body: some View {
ScrollView {
VStack(spacing: 16) {
ForEach(drinkProductViewModel.coffeeProducts) { coffee in
NavigationLink(value: coffee) {
HStack {
Text(coffee.name)
Spacer()
Text("$\(coffee.price)")
Image(systemName: "chevron.right")
.foregroundColor(.gray)
}
}
Divider()
}
.padding()
}
}
.navigationDestination(for: Coffee.self) { coffee in
CoffeeView(
drinkProductViewModel: self.drinkProductViewModel,
coffee: coffee
)
}
.navigationTitle("Coffee Selection")
.onDisappear {
print("CoffeeProductsView disappeared")
print("Nav stack count: \(self.drinkProductViewModel.navPath.count) (CoffeeProductsView)")
}
}
}
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)")
}
}
}
Run Code Online (Sandbox Code Playgroud)
ViewModel(出于虚拟目的......再次,NavigationPath
可以仅存在于根视图中,但这也显示了可能性):
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() {
}
}
Run Code Online (Sandbox Code Playgroud)
最后,重要的是您要考虑这样一个事实:您可以在整个代码库中使用多个枚举,以便您可以.navigationDestination
正确利用。您不需要整个应用程序的视图层次结构存在于一个模型中,否则您可能被迫使用单个模型.navigationDestination
,并且很难将属性或对象传递到子视图中。
归档时间: |
|
查看次数: |
1537 次 |
最近记录: |