简体   繁体   English

在 SwiftUI 中使用 EnvironmentObject 在视图之间传递数据时遇到问题

[英]Trouble passing data between views using EnvironmentObject in SwiftUI

I'm having trouble passing data through different views with EnvironmentObject .我在使用EnvironmentObject通过不同视图传递数据时遇到问题。 I have a file called LineupDetails that contains the following:我有一个名为LineupDetails的文件,其中包含以下内容:

import SwiftUI

class TeamDetails: ObservableObject {
    let characterLimit = 3
    @Published var TeamName: String = "" {
        didSet {
            if TeamName.count > characterLimit {
                TeamName = String(TeamName.prefix(characterLimit))
            }
        }
    }
    @Published var TeamColor: Color = .blue
    @Published var hitter1First: String = ""
    @Published var hitter1Last: String = ""
    @Published var hitter2First: String = ""
    @Published var hitter2Last: String = ""
    @Published var hitter3First: String = ""
    @Published var hitter3Last: String = ""
    @Published var hitter4First: String = ""
    @Published var hitter4Last: String = ""
    @Published var hitter5First: String = ""
    @Published var hitter5Last: String = ""
    @Published var hitter6First: String = ""
    @Published var hitter6Last: String = ""
    @Published var hitter7First: String = ""
    @Published var hitter7Last: String = ""
    @Published var hitter8First: String = ""
    @Published var hitter8Last: String = ""
    @Published var hitter9First: String = ""
    @Published var hitter9Last: String = ""
    
    @Published var Hitter1Pos = "P"
    @Published var Hitter2Pos = "C"
    @Published var Hitter3Pos = "1B"
    @Published var Hitter4Pos = "2B"
    @Published var Hitter5Pos = "3B"
    @Published var Hitter6Pos = "SS"
    @Published var Hitter7Pos = "LF"
    @Published var Hitter8Pos = "CF"
    @Published var Hitter9Pos = "RF"
    
}

These variables are edited through a form in SetHomeLineup .这些变量通过SetHomeLineup的表单进行SetHomeLineup I have excluded the parts of the view not related to the problem, marking them with ... :我已经排除了与问题无关的视图部分,用...标记它们:

struct SetHomeLineup: View {
    @EnvironmentObject var HomeTeam: TeamDetails

...
    
var body: some View {
    HStack {
            TextField("Name", text: $HomeTeam.hitter1First)
            TextField("Last Name", text: $HomeTeam.hitter1Last)
                    Picker(selection: $HomeTeam.Hitter1Pos, label: Text("")) {
                            ForEach(positions, id: \.self) {
                                    Text($0)
                    }
            }
    }
    HStack {
            TextField("Name", text: $HomeTeam.hitter2First)
            TextField("Last Name", text: $HomeTeam.hitter2Last)
                    Picker(selection: $HomeTeam.Hitter2Pos, label: Text("")) {
                            ForEach(positions, id: \.self) {
                                    Text($0)
                    }
            }
    }
        ...
}
// and textfields so on until Hitter9, first and last

Now, when I try to include the inputted values of the above text fields to a different view, with code like this, the view always appears empty to match the default value of the string.现在,当我尝试将上述文本字段的输入值包含到不同的视图中时,使用这样的代码,视图总是显示为空以匹配字符串的默认值。

struct GameView: View {
    @EnvironmentObject var HomeTeam: TeamDetails
    
    ...
    
    var body: some View {
        Text(HomeTeam.hitter1First)
    }
}

Can someone tell me what I'm doing wrong?有人能告诉我我做错了什么吗? I tried using a similar code in a fresh project and it seemed to work just fine, so I'm stumped.我尝试在一个新项目中使用类似的代码,它似乎工作得很好,所以我很难过。

EDIT:编辑:

My views are instantiated like so: The first view of the app is the SetAwayLineup, which includes a NavigationLink to SetHomeLineup like so.我的视图是这样实例化的:应用程序的第一个视图是 SetAwayLineup,它包括一个到 SetHomeLineup 的 NavigationLink,就像这样。

var details = TeamDetails()
NavigationLink(destination: SetHomeLineup().environmentObject(details), isActive: self.$lineupIsReady) {

Similarly, SetHomeLineup includes a navigation link to GameView like so类似地, SetHomeLineup 包含一个指向 GameView 的导航链接,如下所示

var details = TeamDetails()
NavigationLink(destination: GameView().environmentObject(details), isActive: self.$lineupIsReady) {

Both screens have an EnvironmentObject of AwayLineup and HomeLineup that I'm trying to call into GameView.两个屏幕都有一个 AwayLineup 和 HomeLineup 的 EnvironmentObject,我试图将其调用到 GameView 中。

Hopefully this simplifies it希望这可以简化它

The trunk is injected in the NavigationView and doesn't need to be re-injected. trunk是在NavigationView注入的,不需要重新注入。 Even if one of the children doesn't use it.即使其中一个孩子不使用它。 Truck belongs to the NavigationView Truck 属于NavigationView

Then I have created 2 branches A and B that have their own Objects A cannot see B s and vicecersa.然后我创建了 2 个分支AB ,它们有自己的对象A无法看到B和 vicersa。

Each branch has access to their object and the sub-branches (NavigationLink) can be connected to the branch's object by injecting it.每个分支都可以访问它们的对象,并且子分支 (NavigationLink) 可以通过注入它来连接到分支的对象。

import SwiftUI

struct TreeView: View {
    @StateObject var trunk: Trunk = Trunk()
    var body: some View {
        NavigationView{
            List{
                NavigationLink(
                    destination: BranchAView(),
                    label: {
                        Text("BranchA")
                    })
                NavigationLink(
                    destination: BranchBView(),
                    label: {
                        Text("BranchB")
                    })
                
            }.navigationTitle("Trunk")
        }
        //Available to all items in the NavigationView
        //With no need to re-inject for all items of the navView
        .environmentObject(trunk)
        
    }
}
///Has no access to BranchB
struct BranchAView: View {
    @StateObject var branchA: BranchA = BranchA()
    @EnvironmentObject var trunk: Trunk
    var body: some View {
        VStack{
            Text(trunk.title)
            Text(branchA.title)
            NavigationLink(
                destination: BranchAAView()
                    //Initial injection
                    .environmentObject(branchA)
                ,
                label: {
                    Text("Go to Branch AA")
                })
        }.navigationTitle("BranchA")
    }
}
//Has no access to BranchA
struct BranchBView: View {
    @StateObject var branchB: BranchB = BranchB()
    @EnvironmentObject var trunk: Trunk
    var body: some View {
        VStack{
            Text(trunk.title)
            Text(branchB.title)
            NavigationLink(
                destination: BranchBBView()
                    //Initial injection
                    .environmentObject(branchB),
                label: {
                    Text("Go to Branch BB")
                })
        }.navigationTitle("BranchB")
    }
}
struct BranchAAView: View {
    @EnvironmentObject var branchA: BranchA
    @EnvironmentObject var trunk: Trunk
    var body: some View {
        VStack{
            Text(trunk.title)
            Text(branchA.title)
            NavigationLink(
                destination: BranchAAAView()
                    //Needs re-injection because it is a NavigationLink sub-branch
                    .environmentObject(branchA)
                ,
                label: {
                    Text("Go to AAA")
                })
        }.navigationTitle("BranchAA")
    }
}
struct BranchAAAView: View {
    @EnvironmentObject var branchA: BranchA
    @EnvironmentObject var trunk: Trunk
    var body: some View {
        VStack{
            Text(trunk.title)
            Text(branchA.title)
        }.navigationTitle("BranchAAA")
    }
}
struct BranchBBView: View {
    var body: some View {
        VStack{
            Text("I don't need to use the trunk or branch BB")
            //No need to re-inject it is the same branch
            BranchBBBView()
        }.navigationTitle("BranchBB")
    }
}
struct BranchBBBView: View {
    @EnvironmentObject var branchB: BranchB
    @EnvironmentObject var trunk: Trunk
    var body: some View {
        VStack{
            Text("BranchBBBView").font(.title).fontWeight(.bold)
            Text(trunk.title)
            Text(branchB.title)
        }.navigationTitle("BranchBB & BranchBBB")
    }
}
struct TreeView_Previews: PreviewProvider {
    static var previews: some View {
        TreeView()
    }
}
class Trunk: ObservableObject {
    var title: String = "Trunk"
}
class BranchA: ObservableObject {
    var title: String = "BranchA"
}
class BranchB: ObservableObject {
    var title: String = "BranchB"
}

You need to make sure that you're passing the same environment object to any views that need to share the same data.您需要确保将相同的环境对象传递给需要共享相同数据的任何视图。 That means you should create the object and store it in a variable so that you can pass it to both views.这意味着您应该创建对象并将其存储在一个变量中,以便您可以将其传递给两个视图。 From your comments you have:根据您的评论,您有:

NavigationLink(destination: GameView(testUIView: 
    sampleGameViews[0]).environmentObject(TeamDetails()), 
                                          isActive: self.$lineupIsReady { ...

That TeamDetails() constructs a new instance of the TeamDetails class, one that you haven't stored. TeamDetails()构造了一个您尚未存储的TeamDetails类的实例。 That means you must also be doing the same for your SetHomeLineup view.这意味着您也必须对SetHomeLineup视图执行相同的SetHomeLineup Instead, you'll need to create a single instance and keep a reference to it, then pass that reference to any views that you want to share the same data:相反,您需要创建一个实例并保留对它的引用,然后将该引用传递给您想要共享相同数据的任何视图:

var details = TeamDetails()

that should be the only place where you use TeamDetails() ;那应该是您使用TeamDetails()唯一地方; use details when you're setting up your GameView and SetHomeLineup views.使用details ,当你设置你的GameViewSetHomeLineup意见。

Update: Given your edit, the problem is again clear.更新:根据您的编辑,问题再次清晰。 You're still instantiating the TeamDetails class twice, so that the two views still get their own separate instances of that class.您仍在两次实例化TeamDetails类,以便两个视图仍然获得该类的各自独立实例。 They need to use the same instance if they're to share information.如果他们要共享信息,他们需要使用相同的实例。 So there should be only one var details = TeamDetails() line, and the resulting details variable should be used as the environmentObject for both views.所以应该只有一个var details = TeamDetails()行,并且生成的details变量应该用作两个视图的environmentObject

For example, instead of:例如,而不是:

var details = TeamDetails()
NavigationLink(destination: SetHomeLineup().environmentObject(details), isActive: self.$lineupIsReady) {...
//...
var details = TeamDetails()
NavigationLink(destination: GameView().environmentObject(details), isActive: self.$lineupIsReady) {

you want:你要:

var details = TeamDetails()
NavigationLink(destination: SetHomeLineup().environmentObject(details), isActive: self.$lineupIsReady) {...

NavigationLink(destination: GameView().environmentObject(details), isActive: self.$lineupIsReady) {

In general, make sure that you have a clear understanding of the difference between reference types and value types, and between a class and an instance of that class.通常,请确保您清楚了解引用类型和值类型之间以及类和该类的实例之间的区别。 If Mazda Miata is a class, and if I buy a Miata, then the specific car that I've is an instance of the class.如果马自达 Miata是一个类,如果我买了一个 Miata,那么我拥有的特定汽车就是这个类的一个实例。 If I ask you to wash my car, and I ask someone else to change the oil in my car, I'll end up with one car that's clean and has new oil.如果我让你洗我的车,我让别人给我的车换油,我最终会得到一辆干净且有新油的车。 What's going on in your code is that you're buying a Miata and asking someone to wash it, and buying another car and asking someone to change the oil.您的代码中发生的事情是,您正在购买 Miata 并请人洗车,然后购买另一辆车并请人换油。 They're two different cars, so they have different states.他们是两辆不同的车,所以他们有不同的状态。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM