swift EnvironmentObject未为navigationDestination注入

mzsu5hc0  于 2023-03-28  发布在  Swift
关注(0)|答案(3)|浏览(119)

考虑代码

@EnvironmentObject var navModel: NavigationModel
var body: some View {
    someView
       .navigationDestination(for: ImageModel.self) { imageModel in
                ImageDetailedView(image: imageModel)
                    .environmentObject(navModel)   //this is required 
       }
}

导航不被认为是视图的子对象吗?如果是这样,在导航堆栈周围不断抛出environemntObjects是正常的吗?

import Combine
import SwiftUI

enum Destination {
    case firstPage
    case secondPage
}

enum Category: Int, Hashable, CaseIterable, Identifiable, Codable {
    case dessert
    case pancake
    case salad
    case sandwich
    
    var id: Int { rawValue }
    
    var localizedName: LocalizedStringKey {
        switch self {
        case .dessert:
            return "Dessert"
        case .pancake:
            return "Pancake"
        case .salad:
            return "Salad"
        case .sandwich:
            return "Sandwich"
        }
    }
}

@available(iOS 16.0, *)
class Coordinator3: ObservableObject {
    @Published var path = NavigationPath()
    
    func gotoHomePage() {
        path.removeLast(path.count)
    }
    
    func tapOnEnter() {
        path.append(Destination.firstPage)
    }
    
    func tapOnFirstPage() {
        path.append(Destination.secondPage)
    }
    
    func tapOnSecondPage() {
        path.removeLast()
    }
    
    func test() {
        path.removeLast(path.count)
        path.append(2)
    }
}

class Test :ObservableObject {
    var name = "test"
}

@available(iOS 16.0, *)
struct SplitTestView: View {
    @State var selectedCategory: Category?
    var categories = Category.allCases
    @ObservedObject var coordinator = Coordinator3()
    @StateObject var test = Test()
    
    var body: some View {
        NavigationSplitView {
            List(categories, selection: $selectedCategory) { category in
                NavigationLink(category.localizedName, value: category)
            }
        } detail: {
            NavigationStack(path: $coordinator.path) {
                switch selectedCategory {
                case .dessert:
                    Text(selectedCategory!.localizedName)
                case .pancake:
                    VStack {
                        Text("Navigation stack")
                            .padding()
                        NavigationLink("NavigationLink to enter first page", value: Destination.firstPage)
                            .padding()
                        NavigationLink("NavigationLink to enter second page", value: Destination.secondPage)
                            .padding()
                    }
                    .navigationDestination(for: Destination.self) { destination in
                        if destination == .firstPage {
                            FirstPage()
                        } else {
                            Text(
                                "SecondPage()"
                            )
                        }
                    }
                case .salad: Text(selectedCategory!.localizedName)
                case .sandwich: Text(selectedCategory!.localizedName)
                case .none: Text("hi")
                }
            }.environmentObject(test)
        }
    }
}

@available(iOS 16.0, *)
struct SplitTestView_Previews: PreviewProvider {
    static var previews: some View {
        SplitTestView()
    }
}
struct FirstPage: View {
    @EnvironmentObject var test: Test
    var body: some View {
        Text("First Page \(test.name)")
    }
}
r7knjye2

r7knjye21#

这就是为什么MRE很重要,这就是为什么我在第一条评论中提到它,你介绍了NavigationSplitView

  • 情景1*

如果使用NavigationSplitView,则必须将EnvironmentObject注入到NavigationSplitView

NavigationSplitView{
    /*other stuff that includes a navigationDestination*/
}.environmentObject(navModel)
  • 场景2*

如果只使用NavigationStack,则必须在NavigationStack上注入

NavigationStack{
    /*other stuff that includes a navigationDestination*/
}.environmentObject(navModel)
  • 您的样品 *

只需将注入代码向下移动一行。

import Combine
import SwiftUI

enum Destination {
    case firstPage
    case secondPage
}

enum Category: Int, Hashable, CaseIterable, Identifiable, Codable {
    case dessert
    case pancake
    case salad
    case sandwich
    
    var id: Int { rawValue }
    
    var localizedName: LocalizedStringKey {
        switch self {
        case .dessert:
            return "Dessert"
        case .pancake:
            return "Pancake"
        case .salad:
            return "Salad"
        case .sandwich:
            return "Sandwich"
        }
    }
}

@available(iOS 16.0, *)
class Coordinator3: ObservableObject {
    @Published var path = NavigationPath()
    
    func gotoHomePage() {
        path.removeLast(path.count)
    }
    
    func tapOnEnter() {
        path.append(Destination.firstPage)
    }
    
    func tapOnFirstPage() {
        path.append(Destination.secondPage)
    }
    
    func tapOnSecondPage() {
        path.removeLast()
    }
    
    func test() {
        path.removeLast(path.count)
        path.append(2)
    }
}

class Test :ObservableObject {
    var name = "test"
}

@available(iOS 16.0, *)
struct SplitTestView: View {
    @State var selectedCategory: Category?
    var categories = Category.allCases
    @StateObject var coordinator = Coordinator3() //<-- Switch to StateObject
    @StateObject var test = Test()
    
    var body: some View {
        NavigationSplitView {
            List(categories, selection: $selectedCategory) { category in
                NavigationLink(category.localizedName, value: category)
            }
        } detail: {
            NavigationStack(path: $coordinator.path) {
                switch selectedCategory {
                case .dessert:
                    Text(selectedCategory!.localizedName)
                case .pancake:
                    VStack {
                        Text("Navigation stack")
                            .padding()
                        NavigationLink("NavigationLink to enter first page", value: Destination.firstPage)
                            .padding()
                        NavigationLink("NavigationLink to enter second page", value: Destination.secondPage)
                            .padding()
                    }
                    .navigationDestination(for: Destination.self) { destination in
                        if destination == .firstPage {
                            FirstPage()
                        } else {
                            Text(
                                "SecondPage()"
                            )
                        }
                    }
                case .salad: Text(selectedCategory!.localizedName)
                case .sandwich: Text(selectedCategory!.localizedName)
                case .none: Text("hi")
                }
            }
        }.environmentObject(test) //<<--- Add to the NavigationSplitView - The NavigationLink's are presenting in a separate column than the Stack, the only thing they share is the split view.
    }
}

@available(iOS 16.0, *)
struct SplitTestView_Previews: PreviewProvider {
    static var previews: some View {
        SplitTestView()
    }
}
struct FirstPage: View {
    @EnvironmentObject var test: Test
    var body: some View {
        Text("First Page \(test.name)")
    }
}
  • 其他信息 *

在迁移指南中,苹果谈到了这两种类型之间的差异。
https://developer.apple.com/documentation/swiftui/migrating-to-new-navigation-types
他们将NavigationStack的内部称为“内容”

NavigationStack {
    /* content */
}

以及NavigationSplitView“列”的内部

NavigationSplitView {
    /* column 1 */
} content: {
    /* column 2 */
} detail: {
    /* column 3 */
}

在它们各自的设置中,“列”和“内容”仅分别与NavigationLink s/navigationDestination共享NavigationSplitViewNavigationStack
NavigationSplitView内部的NavigationLink在其自己的列中呈现。

注入应该始终发生在最上面的共享View

blpfk2vs

blpfk2vs2#

environmentObjects不通过导航系统是一个已知的行为。你必须每次都手动注入。
在WWDC的休息室里,有人问:
当我将环境对象传递给工作表或NavigationLink时,我遇到过几次环境对象为nil的间歇性崩溃。
答案是:
NavigationLink的设计并没有将EnvironmentObject传递到它的目的地,因为不清楚应该从哪里继承environmentObject。我怀疑这可能是导致你的问题的原因。为了获得你期望的行为,你必须在那个时候显式地传递environmentObject。
https://developer.apple.com/forums/thread/683564

63lcw9qa

63lcw9qa3#

我发现当我的项目变得越来越复杂时,使用EnvironmentObjects会有一些bug。
要做到这一点,在你的类中,列出一个属性static let shared = NavigationModel()。然后,在任何你想使用这个类的视图中,在视图属性中引用它作为@ObservedObject var navModel = NavigationModel.shared
这种方法有利有弊,但我发现利大于弊,因为环境对象往往是错误的。

相关问题