SwiftUI:从详图修改主视图

SwiftUI:从详图修改主视图,swiftui,Swiftui,我想用swiftUI编写我的第一个iOS应用程序。我有很多编程经验,但都是很久以前的事了。我以为我能很快学会斯威夫特,但我错了。世界变了 我有一个包含参与者列表的主视图(一个称为“ActivePerson”的数组),每个参与者都有一个详细视图(“EditPerson”),该视图允许修改参与者或从列表中删除参与者。要添加新人员,列表中的最后一项不是人员,而是指向允许选择新人员的详细视图(“NewPerson”)的符号 目前,我已将人员列表定义为全局变量。当然,这不会导致主视图更新 我尝试过使用@S

我想用swiftUI编写我的第一个iOS应用程序。我有很多编程经验,但都是很久以前的事了。我以为我能很快学会斯威夫特,但我错了。世界变了

我有一个包含参与者列表的主视图(一个称为“ActivePerson”的数组),每个参与者都有一个详细视图(“EditPerson”),该视图允许修改参与者或从列表中删除参与者。要添加新人员,列表中的最后一项不是人员,而是指向允许选择新人员的详细视图(“NewPerson”)的符号

目前,我已将人员列表定义为全局变量。当然,这不会导致主视图更新

我尝试过使用@State包装器,但所有尝试都失败了。当然,我可以在ContentView中将activePersons定义为一个状态变量,但是我不能从细节视图中修改它

非常感谢您的帮助

以下是我的代码(摘录):

而ContentView.swift的内容如下:

import SwiftUI

struct ContentView: View {
    
    var body: some View {
        TabView {
            NavigationView {

                List {
                    ForEach(0..<activePersons.count, id: \.self) { value in
                        NavigationLink(
                            destination: EditPerson(),
                            label: {
                                VStack (alignment: .leading) {
                                    Text(activePersons[value].name)
                                        .font(.system(size: 26, weight: .regular))
                         
                                }.padding(15)
                            })
                    }
                    NavigationLink(
                        destination: NewPerson(),
                        label: {
                            Image(systemName: "person.crop.circle.badge.plus")
                                .font(.system(size: 26, weight: .light))
                                .foregroundColor(Color(red: 0.7, green: 0.7, blue: 0.7))
                                .padding(15)
                        })
                }.environment(\.defaultMinListRowHeight, 30)
                .navigationTitle("Participants")
            }
            .tabItem{
                Image(systemName: "square.and.pencil")
                Text("Planning")
            }
...
导入快捷界面
结构ContentView:View{
var body:一些观点{
TabView{
导航视图{
名单{

ForEach(0..通常,您需要使用绑定或observeObject将状态向下传递到子视图中。下面的代码说明了这两种情况:


struct Person: Identifiable, Hashable {
    let id: Int
    var name: String
}

class AppState : ObservableObject {
    @Published var activeIds : [Int] = [0,1]
    @Published var persons: [Person] = [Person(id: 0, name: "Dummy1"),
                                    Person(id: 1, name: "Dummy2"),
                                    Person(id: 2, name: "Dummy3"),
                                    Person(id: 3, name: "Dummy4")]
    
    var activePersons : [Person] {
        return activeIds.compactMap { id in
            persons.first(where: { $0.id == id
            })
        }
    }
    
    var nonActivePersons : [Person] {
        persons.filter { !activeIds.contains($0.id) }
    }
    
    func bindingForId(id: Int) -> Binding<Person> {
        .init { () -> Person in
            self.persons.first(where: { $0.id == id }) ?? Person(id: -1, name: "()")
        } set: { (newValue) in
            self.persons = self.persons.map {
                if $0.id == id {
                    return newValue
                } else {
                    return $0
                }
            }
        }

    }
}

struct ContentView: View {
    @StateObject var state = AppState()
    
    var body: some View {
        TabView {
            NavigationView {
                List {
                    ForEach(state.activePersons, id: \.self) { person in
                        NavigationLink(
                            destination: EditPerson(person: state.bindingForId(id: person.id)),
                            label: {
                                VStack (alignment: .leading) {
                                    Text(person.name)
                                        .font(.system(size: 26, weight: .regular))
                         
                                }.padding(15)
                            })
                    }
                    NavigationLink(
                        destination: NewPerson(state: state),
                        label: {
                            Image(systemName: "person.crop.circle.badge.plus")
                                .font(.system(size: 26, weight: .light))
                                .foregroundColor(Color(red: 0.7, green: 0.7, blue: 0.7))
                                .padding(15)
                        })
                }.environment(\.defaultMinListRowHeight, 30)
                .navigationTitle("Participants")
            }
            .tabItem{
                Image(systemName: "square.and.pencil")
                Text("Planning")
            }
        }
    }
}

struct EditPerson : View {
    @Binding var person : Person
    
    var body: some View {
        TextField("name", text: $person.name)
    }
}

struct NewPerson: View {
    @ObservedObject var state : AppState
    var body: some View {
        List {
            ForEach(state.nonActivePersons, id: \.self) { person in
                Button(action: {
                    state.activeIds.append(person.id)
                }) {
                    Text(person.name)
                }
            }
        }.environment(\.defaultMinListRowHeight, 30)
    }
}


结构人:可识别、可散列{
让id:Int
变量名称:String
}
类AppState:ObservableObject{
@已发布的var-activeIds:[Int]=[0,1]
@已发布var人员:[人员]=[人员(id:0,名称:“Dummy1”),
此人(id:1,姓名:“Dummy2”),
此人(id:2,姓名:“Dummy3”),
人员(身份证号码:3,姓名:“Dummy4”)]
个人:[个人]{
返回activeIds.compactMap{id
persons.first(其中:{$0.id==id
})
}
}
变量非活动人员:[人]{
persons.filter{!activeIds.contains($0.id)}
}
func bindingForId(id:Int)->Binding{
.init{()->中的个人
Person.first(其中:{$0.id==id})?Person(id:-1,名称:“()”)
}集合:{(newValue)在
self.persons=self.persons.map{
如果$0.id==id{
返回新值
}否则{
返回$0
}
}
}
}
}
结构ContentView:View{
@StateObject变量state=AppState()
var body:一些观点{
TabView{
导航视图{
名单{
ForEach(state.activePersons,id:\.self){person in
导航链接(
目标:EditPerson(person:state.bindingForId(id:person.id)),
标签:{
VStack(对齐:。前导){
文本(person.name)
.font(.system(大小:26,重量:常规))
}.填充(15)
})
}
导航链接(
目的地:NewPerson(州:州),
标签:{
图像(系统名称:“person.crop.circle.badge.plus”)
.font(.system(大小:26,重量:轻))
.foregroundColor(颜色(红色:0.7,绿色:0.7,蓝色:0.7))
.填充(15)
})
}.environment(\.defaultMinListRowHeight,30)
.navigationTitle(“参与者”)
}
.tabItem{
图像(系统名称:“正方形和铅笔”)
文本(“规划”)
}
}
}
}
结构编辑人员:视图{
@个人:个人
var body:一些观点{
TextField(“name”,text:$person.name)
}
}
结构NewPerson:View{
@ObservedObject变量状态:AppState
var body:一些观点{
名单{
ForEach(state.nonActivePersons,id:\.self){person in
按钮(操作:{
state.activeIds.append(person.id)
}) {
文本(person.name)
}
}
}.environment(\.defaultMinListRowHeight,30)
}
}
在本例中,我正在创建一个名为
AppState
的observeObject。它具有@Published属性--@Published将在值发生更改时发出刷新视图的信号()

您可以看到,当调用
NewPerson
时,整个state对象作为参数传递给它

对于
EditPerson
视图,我只将
绑定
传递给
Person
——绑定允许在SwiftUI中进行双向通信()

因为您有一个
人的数组
,所以我必须做一些工作才能获得直接绑定,除非您想使用数组索引,因为数组索引有点脆弱。您可以在函数
bindingForId
中看到我如何获得基于id的绑定

我还有
活动人员
非活动人员
的计算属性——当其中一个
@已发布的
值发生更改时,这些计算属性将重新计算



你可能会发现我的另一个答案是:

这很好用,谢谢!但是,有一件事还不完美:当我选择一个新的人时,视图临时切换到父视图,然后再次切换到新的人视图。就我而言,完美的选择是,在我选择了一个新的人之后,视图更改以编辑此人的视图。是否可以从一个详细视图切换到另一个详细视图?
import SwiftUI

struct NewPerson: View {
    @State var name: String = ""
    var body: some View {
        List {
            ForEach(0..<otherPersons.count) { value in
                Button(action: {
                    activePersons.append(otherPersons[value])
                }) {
                Text(otherPersons[value].name)
                }
            }
        }.environment(\.defaultMinListRowHeight, 30)
    }
}

struct Person: Identifiable, Hashable {
    let id: Int
    var name: String
}

class AppState : ObservableObject {
    @Published var activeIds : [Int] = [0,1]
    @Published var persons: [Person] = [Person(id: 0, name: "Dummy1"),
                                    Person(id: 1, name: "Dummy2"),
                                    Person(id: 2, name: "Dummy3"),
                                    Person(id: 3, name: "Dummy4")]
    
    var activePersons : [Person] {
        return activeIds.compactMap { id in
            persons.first(where: { $0.id == id
            })
        }
    }
    
    var nonActivePersons : [Person] {
        persons.filter { !activeIds.contains($0.id) }
    }
    
    func bindingForId(id: Int) -> Binding<Person> {
        .init { () -> Person in
            self.persons.first(where: { $0.id == id }) ?? Person(id: -1, name: "()")
        } set: { (newValue) in
            self.persons = self.persons.map {
                if $0.id == id {
                    return newValue
                } else {
                    return $0
                }
            }
        }

    }
}

struct ContentView: View {
    @StateObject var state = AppState()
    
    var body: some View {
        TabView {
            NavigationView {
                List {
                    ForEach(state.activePersons, id: \.self) { person in
                        NavigationLink(
                            destination: EditPerson(person: state.bindingForId(id: person.id)),
                            label: {
                                VStack (alignment: .leading) {
                                    Text(person.name)
                                        .font(.system(size: 26, weight: .regular))
                         
                                }.padding(15)
                            })
                    }
                    NavigationLink(
                        destination: NewPerson(state: state),
                        label: {
                            Image(systemName: "person.crop.circle.badge.plus")
                                .font(.system(size: 26, weight: .light))
                                .foregroundColor(Color(red: 0.7, green: 0.7, blue: 0.7))
                                .padding(15)
                        })
                }.environment(\.defaultMinListRowHeight, 30)
                .navigationTitle("Participants")
            }
            .tabItem{
                Image(systemName: "square.and.pencil")
                Text("Planning")
            }
        }
    }
}

struct EditPerson : View {
    @Binding var person : Person
    
    var body: some View {
        TextField("name", text: $person.name)
    }
}

struct NewPerson: View {
    @ObservedObject var state : AppState
    var body: some View {
        List {
            ForEach(state.nonActivePersons, id: \.self) { person in
                Button(action: {
                    state.activeIds.append(person.id)
                }) {
                    Text(person.name)
                }
            }
        }.environment(\.defaultMinListRowHeight, 30)
    }
}