如何在没有 NavigationLink 的情况下导航到 SwiftUI 中的视图
How to navigate to a View in SwiftUI without NavigationLink
我是 SwiftUI 的新手,想从任何其他视图导航到 SwiftUI 中的“主页”视图。我创建了一个主页按钮,该按钮被添加到多个视图的导航栏中。主页按钮 returns 到“主页”视图,但导航链接无法正确导航并显示错误数据。
我已经通读了其他有类似问题的人的多个答案,但这些答案至少已有两年历史,而且 SwiftUI 从那时起发生了很大变化。我已经尝试实现许多答案,但总是 运行 解决其他问题。某些功能已被弃用,我正在寻找更具前瞻性的解决方案。
下面是一些显示问题的代码。在此示例中,我在 NavigationLink 中使用 isActive 参数来触发主页按钮,但这似乎会破坏 NavigationLink。我也尝试过使用标签和选择参数并获得相同的结果。通过阅读其他回复,我相信在“ProductFamilyView”中使用 ForEach 语句是引用错误数据的问题。但是,在这个应用程序中,我需要使用 ForEach 语句来显示产品系列。
如果我尝试调用 ProductFamilyView(),没有任何反应。在此示例中,我想导航回“ProductFamilyView”。是否有另一种方法可以在不使用 NavigationLink 的情况下导航到此视图?
import SwiftUI
import Combine
class Navigation: ObservableObject {
@Published var productFamilyIsActive : Bool = false
}
struct ContentView: View {
@StateObject var navigation = Navigation()
var body: some View {
NavigationView {
VStack {
LoginButtonView(buttonText: "Login")
}
}
.environmentObject(navigation)
.navigationViewStyle(StackNavigationViewStyle() )
}
}
struct LoginButtonView: View {
@State private var navigated : Bool = false
var buttonText : String
var body: some View {
Button(action: {
self.navigated.toggle()
}, label: {
Text("\(buttonText)")
})
.navigationBarBackButtonHidden(true)
NavigationLink(destination: ProductFamilyView(), isActive: $navigated ) {
EmptyView()
}
}
}
struct ProductFamilyView: View { //This is my home view
private var productFamilies = ProductFamilyViewModel.loadData()
var body: some View {
List {
ForEach(productFamilies) { (product) in
ProductFamilyRow(productFamily: product.productFamily)
}
}
.navigationTitle("Product Families")
.navigationBarTitleDisplayMode(.inline)
.navigationBarBackButtonHidden(true)
}
}
struct ProductFamilyRow: View {
@EnvironmentObject var navigation : Navigation
@State var productFamily : String
var body: some View {
NavigationLink(destination: PartNumberView(productFamily: productFamily), isActive: $navigation.productFamilyIsActive) {
Text("\(productFamily)")
}
.isDetailLink(false)
}
}
struct PartNumberView: View {
@State var productFamily: String
var productDetails = ProductViewModel.loadData()
var body: some View {
List {
ForEach(productDetails) { (productDetail) in
if productFamily == productDetail.productFamily {
NavigationLink(destination: PartNumberRow(productDetail: productDetail)) {
Text("\(productDetail.partNumber)")
}
}
}
}
.navigationTitle("\(productFamily)")
.navigationBarItems(trailing: HomeButtonView() )
}
}
struct PartNumberRow: View {
@State var productDetail : ProductViewModel
var body: some View {
List {
Text("Description: \(productDetail.description)")
NavigationLink(destination: ColorView(colorOptions: productDetail.colorOptions)) {
Text("Color Options")
}
}
.navigationTitle("\(productDetail.partNumber)")
.navigationBarItems(trailing: HomeButtonView() )
}
}
struct ColorView: View {
@State var colorOptions : [Item]
var body: some View {
List {
ForEach(colorOptions) { (color) in
Text("\(color.name)")
}
}
.navigationBarItems(trailing: HomeButtonView() )
}
}
struct HomeButtonView: View {
@EnvironmentObject var navigation : Navigation
var body: some View {
Button(action: {
self.navigation.productFamilyIsActive.toggle()
}, label: {
Image(systemName: "house")
})
.environmentObject(navigation)
}
}
class ProductFamilyViewModel: ObservableObject, Identifiable {
var id = UUID().uuidString
var productFamily : String = ""
init(productFamily: String) {
self.productFamily = productFamily
}
static func loadData() -> [ProductFamilyViewModel] {
let products = ["Product Family 1", "Product Family 2", "Product Family 3"]
var productFamilies : [ProductFamilyViewModel] = []
for product in products {
productFamilies.append(ProductFamilyViewModel(productFamily: product))
}
return productFamilies
}
}
struct Item: Identifiable, Codable {
var id : String {
self.name
}
var name : String
}
struct Product: Identifiable {
var id = UUID().uuidString
var partNumber : String = "part number"
var productFamily : String = "product family"
var description : String = "description"
var colorOptions : [Item] = []
}
class ProductViewModel: ObservableObject, Identifiable {
var id = UUID().uuidString
var partNumber : String
var productFamily : String
var description : String
var colorOptions : [Item]
init(model: Product) {
self.partNumber = model.partNumber
self.productFamily = model.productFamily
self.description = model.description
self.colorOptions = model.colorOptions
}
static func loadData() -> [ProductViewModel] {
let colors = [Item(name: "red"), Item(name: "white"), Item(name: "blue")]
let productDetails : [ProductViewModel] = [
ProductViewModel( model: Product(
partNumber: "100-1111-101",
productFamily: "Product Family 1",
description: "Part Number 100-1111-101",
colorOptions: colors)
),
ProductViewModel( model: Product(
partNumber: "100-1111-102",
productFamily: "Product Family 1",
description: "Part Number 100-1111-102",
colorOptions: colors)
),
ProductViewModel( model: Product(
partNumber: "100-1111-103",
productFamily: "Product Family 1",
description: "Part Number 100-1111-103",
colorOptions: colors)
),
ProductViewModel( model: Product(
partNumber: "200-1111-101",
productFamily: "Product Family 2",
description: "Part Number 200-1111-101",
colorOptions: colors)
),
ProductViewModel( model: Product(
partNumber: "200-1111-102",
productFamily: "Product Family 2",
description: "Part Number 200-1111-102",
colorOptions: colors)
),
ProductViewModel( model: Product(
partNumber: "200-1111-103",
productFamily: "Product Family 2",
description: "Part Number 200-1111-103",
colorOptions: colors)
),
ProductViewModel( model: Product(
partNumber: "300-1111-101",
productFamily: "Product Family 3",
description: "Part Number 300-1111-101",
colorOptions: colors)
),
ProductViewModel( model: Product(
partNumber: "300-1111-102",
productFamily: "Product Family 3",
description: "Part Number 300-1111-102",
colorOptions: colors)
),
ProductViewModel( model: Product(
partNumber: "300-1111-103",
productFamily: "Product Family 3",
description: "Part Number 300-1111-103",
colorOptions: colors)
)
]
return productDetails
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
.environmentObject(Navigation())
}
}
首先 - 恭喜:这是第一个如此复杂的代码,它在 copy/paste 上完美地运行到 Xcode ......不经常发生 :)
你的问题:
你几乎拥有它。唯一的问题是你的 productFamilyIsActive
是一个布尔值——它只能保存一个选定的 NavigationLink 上的信息,你有 3 个。但是如果你将它更改为可选选择,它就可以:
class Navigation: ObservableObject {
@Published var productFamilyIsActive : String? = nil // here
}
为此,您在此处的 NavigationLink 上使用不同的初始化程序:
struct ProductFamilyRow: View {
@EnvironmentObject var navigation : Navigation
@State var productFamily : String
var body: some View {
NavigationLink(destination: PartNumberView(productFamily: productFamily),
tag: productFamily, // here
selection: $navigation.productFamilyIsActive) { // and here
Text("\(productFamily)")
}
.isDetailLink(false)
}
}
最后你没有设置为 false 而是在这里设置为 nil:
struct HomeButtonView: View {
@EnvironmentObject var navigation : Navigation
var body: some View {
Button(action: {
self.navigation.productFamilyIsActive = nil // here
}, label: {
Image(systemName: "house")
})
.environmentObject(navigation)
}
}
瞧!
我是 SwiftUI 的新手,想从任何其他视图导航到 SwiftUI 中的“主页”视图。我创建了一个主页按钮,该按钮被添加到多个视图的导航栏中。主页按钮 returns 到“主页”视图,但导航链接无法正确导航并显示错误数据。
我已经通读了其他有类似问题的人的多个答案,但这些答案至少已有两年历史,而且 SwiftUI 从那时起发生了很大变化。我已经尝试实现许多答案,但总是 运行 解决其他问题。某些功能已被弃用,我正在寻找更具前瞻性的解决方案。
下面是一些显示问题的代码。在此示例中,我在 NavigationLink 中使用 isActive 参数来触发主页按钮,但这似乎会破坏 NavigationLink。我也尝试过使用标签和选择参数并获得相同的结果。通过阅读其他回复,我相信在“ProductFamilyView”中使用 ForEach 语句是引用错误数据的问题。但是,在这个应用程序中,我需要使用 ForEach 语句来显示产品系列。
如果我尝试调用 ProductFamilyView(),没有任何反应。在此示例中,我想导航回“ProductFamilyView”。是否有另一种方法可以在不使用 NavigationLink 的情况下导航到此视图?
import SwiftUI
import Combine
class Navigation: ObservableObject {
@Published var productFamilyIsActive : Bool = false
}
struct ContentView: View {
@StateObject var navigation = Navigation()
var body: some View {
NavigationView {
VStack {
LoginButtonView(buttonText: "Login")
}
}
.environmentObject(navigation)
.navigationViewStyle(StackNavigationViewStyle() )
}
}
struct LoginButtonView: View {
@State private var navigated : Bool = false
var buttonText : String
var body: some View {
Button(action: {
self.navigated.toggle()
}, label: {
Text("\(buttonText)")
})
.navigationBarBackButtonHidden(true)
NavigationLink(destination: ProductFamilyView(), isActive: $navigated ) {
EmptyView()
}
}
}
struct ProductFamilyView: View { //This is my home view
private var productFamilies = ProductFamilyViewModel.loadData()
var body: some View {
List {
ForEach(productFamilies) { (product) in
ProductFamilyRow(productFamily: product.productFamily)
}
}
.navigationTitle("Product Families")
.navigationBarTitleDisplayMode(.inline)
.navigationBarBackButtonHidden(true)
}
}
struct ProductFamilyRow: View {
@EnvironmentObject var navigation : Navigation
@State var productFamily : String
var body: some View {
NavigationLink(destination: PartNumberView(productFamily: productFamily), isActive: $navigation.productFamilyIsActive) {
Text("\(productFamily)")
}
.isDetailLink(false)
}
}
struct PartNumberView: View {
@State var productFamily: String
var productDetails = ProductViewModel.loadData()
var body: some View {
List {
ForEach(productDetails) { (productDetail) in
if productFamily == productDetail.productFamily {
NavigationLink(destination: PartNumberRow(productDetail: productDetail)) {
Text("\(productDetail.partNumber)")
}
}
}
}
.navigationTitle("\(productFamily)")
.navigationBarItems(trailing: HomeButtonView() )
}
}
struct PartNumberRow: View {
@State var productDetail : ProductViewModel
var body: some View {
List {
Text("Description: \(productDetail.description)")
NavigationLink(destination: ColorView(colorOptions: productDetail.colorOptions)) {
Text("Color Options")
}
}
.navigationTitle("\(productDetail.partNumber)")
.navigationBarItems(trailing: HomeButtonView() )
}
}
struct ColorView: View {
@State var colorOptions : [Item]
var body: some View {
List {
ForEach(colorOptions) { (color) in
Text("\(color.name)")
}
}
.navigationBarItems(trailing: HomeButtonView() )
}
}
struct HomeButtonView: View {
@EnvironmentObject var navigation : Navigation
var body: some View {
Button(action: {
self.navigation.productFamilyIsActive.toggle()
}, label: {
Image(systemName: "house")
})
.environmentObject(navigation)
}
}
class ProductFamilyViewModel: ObservableObject, Identifiable {
var id = UUID().uuidString
var productFamily : String = ""
init(productFamily: String) {
self.productFamily = productFamily
}
static func loadData() -> [ProductFamilyViewModel] {
let products = ["Product Family 1", "Product Family 2", "Product Family 3"]
var productFamilies : [ProductFamilyViewModel] = []
for product in products {
productFamilies.append(ProductFamilyViewModel(productFamily: product))
}
return productFamilies
}
}
struct Item: Identifiable, Codable {
var id : String {
self.name
}
var name : String
}
struct Product: Identifiable {
var id = UUID().uuidString
var partNumber : String = "part number"
var productFamily : String = "product family"
var description : String = "description"
var colorOptions : [Item] = []
}
class ProductViewModel: ObservableObject, Identifiable {
var id = UUID().uuidString
var partNumber : String
var productFamily : String
var description : String
var colorOptions : [Item]
init(model: Product) {
self.partNumber = model.partNumber
self.productFamily = model.productFamily
self.description = model.description
self.colorOptions = model.colorOptions
}
static func loadData() -> [ProductViewModel] {
let colors = [Item(name: "red"), Item(name: "white"), Item(name: "blue")]
let productDetails : [ProductViewModel] = [
ProductViewModel( model: Product(
partNumber: "100-1111-101",
productFamily: "Product Family 1",
description: "Part Number 100-1111-101",
colorOptions: colors)
),
ProductViewModel( model: Product(
partNumber: "100-1111-102",
productFamily: "Product Family 1",
description: "Part Number 100-1111-102",
colorOptions: colors)
),
ProductViewModel( model: Product(
partNumber: "100-1111-103",
productFamily: "Product Family 1",
description: "Part Number 100-1111-103",
colorOptions: colors)
),
ProductViewModel( model: Product(
partNumber: "200-1111-101",
productFamily: "Product Family 2",
description: "Part Number 200-1111-101",
colorOptions: colors)
),
ProductViewModel( model: Product(
partNumber: "200-1111-102",
productFamily: "Product Family 2",
description: "Part Number 200-1111-102",
colorOptions: colors)
),
ProductViewModel( model: Product(
partNumber: "200-1111-103",
productFamily: "Product Family 2",
description: "Part Number 200-1111-103",
colorOptions: colors)
),
ProductViewModel( model: Product(
partNumber: "300-1111-101",
productFamily: "Product Family 3",
description: "Part Number 300-1111-101",
colorOptions: colors)
),
ProductViewModel( model: Product(
partNumber: "300-1111-102",
productFamily: "Product Family 3",
description: "Part Number 300-1111-102",
colorOptions: colors)
),
ProductViewModel( model: Product(
partNumber: "300-1111-103",
productFamily: "Product Family 3",
description: "Part Number 300-1111-103",
colorOptions: colors)
)
]
return productDetails
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
.environmentObject(Navigation())
}
}
首先 - 恭喜:这是第一个如此复杂的代码,它在 copy/paste 上完美地运行到 Xcode ......不经常发生 :)
你的问题:
你几乎拥有它。唯一的问题是你的 productFamilyIsActive
是一个布尔值——它只能保存一个选定的 NavigationLink 上的信息,你有 3 个。但是如果你将它更改为可选选择,它就可以:
class Navigation: ObservableObject {
@Published var productFamilyIsActive : String? = nil // here
}
为此,您在此处的 NavigationLink 上使用不同的初始化程序:
struct ProductFamilyRow: View {
@EnvironmentObject var navigation : Navigation
@State var productFamily : String
var body: some View {
NavigationLink(destination: PartNumberView(productFamily: productFamily),
tag: productFamily, // here
selection: $navigation.productFamilyIsActive) { // and here
Text("\(productFamily)")
}
.isDetailLink(false)
}
}
最后你没有设置为 false 而是在这里设置为 nil:
struct HomeButtonView: View {
@EnvironmentObject var navigation : Navigation
var body: some View {
Button(action: {
self.navigation.productFamilyIsActive = nil // here
}, label: {
Image(systemName: "house")
})
.environmentObject(navigation)
}
}
瞧!