SwiftUI 中传递数据的多维列表
Multidimensional lists in SwiftUI that pass data
我有这个型号:
struct Training: Identifiable {
let id = UUID()
let name: String
let workout: [Workout]?
}
和:
struct Workout: Identifiable {
let id = UUID()
let name: String
let exercices: [Exercice]?
}
和:
struct Exercice: Identifiable {
let id = UUID()
let name: String
}
模型的数据来自环境对象。
该应用程序将以空的培训列表启动,您可以在 UI 中添加培训。每个训练都有一个导航链接到一个视图,以便为每个训练添加锻炼,在下一步中,您可以为每个锻炼添加锻炼。
在我的逻辑中,我使用上面显示的结构创建多维数组。
培训视图很简单:
struct TrainingsView: View {
@EnvironmentObject var appState: AppState
@State var showingDetail = false
var body: some View {
NavigationView {
VStack {
List {
ForEach (appState.trainings) { training in
NavigationLink(destination: WorkoutsView(training: training).environmentObject(self.appState)) {
Text(training.name)
}
}
.onDelete(perform: appState.removeTraining)
}
// Button to add trainings....
.navigationBarTitle(Text("Trainings").foregroundColor(Color.white))
}
}
}
}
WorkoutsView
看起来一样,但我在列出家长培训项目时遇到问题:
struct WorkoutsView: View {
// ...
var training: Training
var body: some View {
VStack {
List {
ForEach (appState.trainings(training).workouts) { workout in // I know the appState call is incorrect, but I don't know how to access is correctly.
NavigationLink(destination: ExercicesView(workout)) {
Text(workout.name)
}
}
}
// ...
}
}
}
我已经试过了:
List {
ForEach (0 ..< appState.trainings.count) {
NavigationLink(destination: WorkoutsView(training: [=15=]).environmentObject(self.appState)) {
Text(appState.trainings[[=15=]].name)
}
}
}
我可以在 WorkoutsView
中使用 appState.trainings[training].workouts
,但我在 NavigationLink 行收到错误 Contextual closure type '() -> Text' expects 0 arguments, but 1 was used in closure body
,不知道该怎么做。
附加问题:如果这接近于解决方案,我不需要结构符合 Identifiable?
这里有 2 种广泛的方法,具体取决于您希望如何设计系统。
1. 您的 child 视图知道应用程序状态并可以直接修改它。所以,parent需要通过indices/keys让child定位修改哪些数据:
struct TrainingsView: View {
@EnvironmentObject var appState: AppState
var body: some View {
NavigationView {
List(0..<appState.trainings.count) { i in
NavigationLink(destination: WorkoutsView(trainingIndex: i)) {
Text(self.appState.trainings[i].name)
}
}
}
}
}
struct WorkoutsView: View {
var trainingIdx: Int
@EnvironmentObject var appState: AppState
var body: some View {
VStack() {
TextField("training name: ", text: $appState.trainings[trainingIdx].name)
Button(action: {self.appState.trainings[trainingIdx].workouts.append(Workout(...))}) {
Text("Add workout")
}
}
}
}
2. 或者,您可能会说您不希望 child 视图了解应用程序的状态 - 您只希望它们修改一些静态的struct
他们不拥有(而是属于他们的 parent),那么你应该使用 @Binding
.
下面的例子是为了说明一个观点的概念:
struct TrainingsView: View {
@State var trainingA = Training(...)
@State var trainingB = Training(...)
var body: some Body {
NavigationView {
List {
WorkoutsView(training: $trainingA)
WorkoutsView(training: $trainingB)
}
}
}
}
struct WorkoutsView: View {
@Binding var training: Training
var body: some View {
VStack() {
TextField("training name: ", text: $training.name)
Button(action: { self.training.workouts.append(Workout(...)) }) {
Text("Add workout")
}
}
}
}
我有这个型号:
struct Training: Identifiable {
let id = UUID()
let name: String
let workout: [Workout]?
}
和:
struct Workout: Identifiable {
let id = UUID()
let name: String
let exercices: [Exercice]?
}
和:
struct Exercice: Identifiable {
let id = UUID()
let name: String
}
模型的数据来自环境对象。
该应用程序将以空的培训列表启动,您可以在 UI 中添加培训。每个训练都有一个导航链接到一个视图,以便为每个训练添加锻炼,在下一步中,您可以为每个锻炼添加锻炼。
在我的逻辑中,我使用上面显示的结构创建多维数组。
培训视图很简单:
struct TrainingsView: View {
@EnvironmentObject var appState: AppState
@State var showingDetail = false
var body: some View {
NavigationView {
VStack {
List {
ForEach (appState.trainings) { training in
NavigationLink(destination: WorkoutsView(training: training).environmentObject(self.appState)) {
Text(training.name)
}
}
.onDelete(perform: appState.removeTraining)
}
// Button to add trainings....
.navigationBarTitle(Text("Trainings").foregroundColor(Color.white))
}
}
}
}
WorkoutsView
看起来一样,但我在列出家长培训项目时遇到问题:
struct WorkoutsView: View {
// ...
var training: Training
var body: some View {
VStack {
List {
ForEach (appState.trainings(training).workouts) { workout in // I know the appState call is incorrect, but I don't know how to access is correctly.
NavigationLink(destination: ExercicesView(workout)) {
Text(workout.name)
}
}
}
// ...
}
}
}
我已经试过了:
List {
ForEach (0 ..< appState.trainings.count) {
NavigationLink(destination: WorkoutsView(training: [=15=]).environmentObject(self.appState)) {
Text(appState.trainings[[=15=]].name)
}
}
}
我可以在 WorkoutsView
中使用 appState.trainings[training].workouts
,但我在 NavigationLink 行收到错误 Contextual closure type '() -> Text' expects 0 arguments, but 1 was used in closure body
,不知道该怎么做。
附加问题:如果这接近于解决方案,我不需要结构符合 Identifiable?
这里有 2 种广泛的方法,具体取决于您希望如何设计系统。
1. 您的 child 视图知道应用程序状态并可以直接修改它。所以,parent需要通过indices/keys让child定位修改哪些数据:
struct TrainingsView: View {
@EnvironmentObject var appState: AppState
var body: some View {
NavigationView {
List(0..<appState.trainings.count) { i in
NavigationLink(destination: WorkoutsView(trainingIndex: i)) {
Text(self.appState.trainings[i].name)
}
}
}
}
}
struct WorkoutsView: View {
var trainingIdx: Int
@EnvironmentObject var appState: AppState
var body: some View {
VStack() {
TextField("training name: ", text: $appState.trainings[trainingIdx].name)
Button(action: {self.appState.trainings[trainingIdx].workouts.append(Workout(...))}) {
Text("Add workout")
}
}
}
}
2. 或者,您可能会说您不希望 child 视图了解应用程序的状态 - 您只希望它们修改一些静态的struct
他们不拥有(而是属于他们的 parent),那么你应该使用 @Binding
.
下面的例子是为了说明一个观点的概念:
struct TrainingsView: View {
@State var trainingA = Training(...)
@State var trainingB = Training(...)
var body: some Body {
NavigationView {
List {
WorkoutsView(training: $trainingA)
WorkoutsView(training: $trainingB)
}
}
}
}
struct WorkoutsView: View {
@Binding var training: Training
var body: some View {
VStack() {
TextField("training name: ", text: $training.name)
Button(action: { self.training.workouts.append(Workout(...)) }) {
Text("Add workout")
}
}
}
}