如何在没有 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)
    }
}

瞧!