Swift 如何变异从其他视图传递的变量

Swift 如何变异从其他视图传递的变量,swift,swiftui,Swift,Swiftui,我是SwiftUI新手,我尝试创建一个应用程序,它有一个目标列表,在列表上方,有一个添加按钮来添加目标并将其显示在列表上。目前,我在将目标实例添加到目标(目标数组)中遇到问题,在“创建”视图中,我尝试将新的目标实例附加到我在另一个视图中创建的目标中。它给了我一条错误消息:不能在不可变值上使用mutating member:“self”在goals.append(Goal(…)行上是不可变的?这是我的密码!非常感谢你 struct ContentView: View { var goals

我是SwiftUI新手,我尝试创建一个应用程序,它有一个目标列表,在列表上方,有一个添加按钮来添加目标并将其显示在列表上。目前,我在将目标实例添加到目标(目标数组)中遇到问题,在“创建”视图中,我尝试将新的目标实例附加到我在另一个视图中创建的目标中。它给了我一条错误消息:不能在不可变值上使用mutating member:“self”在goals.append(Goal(…)行上是不可变的?这是我的密码!非常感谢你

struct ContentView: View {
    var goals: [Goal] = []
    
    var body: some View {
        TabView{
            VStack{
                Text("You have")
                Text("0")
                Text("tasks to do")
            }.tabItem { Text("Home")}
            MyScroll(1..<100).tabItem { Text("My Goals") }
        }
    }
}

struct MyScroll: View {
    var numRange: Range<Int>
    var goals: [Goal]
    
    init (_ r:Range<Int>) {
        numRange = r
        goals = []
    }
    
    var body: some View {
        NavigationView{
            VStack{
                NavigationLink(destination: AddView(goals:self.goals)){
                    Image(systemName: "folder.badge.plus")
                }
                List(goals) { goal in
                    HStack(alignment: .center){
                        Text(goal.name)
                    }
                }
            }
        }.navigationTitle(Text("1111"))
    }
}

struct AddView: View {
    var goals:[Goal]
    @State var types = ["study", "workout", "hobby", "habbit"]
    @State private var selected = false
    @State var selection = Set<String>()
    @State var goalName: String = ""
    @State var goalType: String = ""
    @State var isLongTerm: Bool = false
    @State var progress: [Progress] = []
    
    var body: some View {
        VStack{
            Text("Create your goal")
            // type in name
            HStack{
                TextField("Name", text: $goalName)
            }.padding()
            // choose type: a selection list
            HStack{
                List(types, id: \.self, selection: $selection) {
                    Text($0)
                }
                .navigationBarItems(trailing: EditButton())
            }.padding()
            // toggle if it is a logn term goal
            HStack{
                Toggle(isOn: $selected) {
                    Text("Is your goal Long Term (no end date)")
                }.padding()
            }.padding()
            Button(action: {
                addGoal(goalName, goalType, isLongTerm, progress)
            }, label: {
                /*@START_MENU_TOKEN@*/Text("Button")/*@END_MENU_TOKEN@*/
            })
        }
    }
        
    // function that add the goal instance to the goals
    mutating func addGoal( _ t:String, _ n:String, _ iLT: Bool, _ p: [Progress]){
        let item: Goal = Goal(t,n,iLT,[])
        goals.append(item)
    }
}

一种方法是使用
@Binding
将@State保存在父视图中,并将其向下传递到视图层次结构中,让子视图将数据发送回

(一个警告是,在当前版本的SwiftUI中,通过多个视图发送绑定可能会产生意外的结果,但一个或两个级别似乎可以。另一个选项是使用带有@Published属性的ObserveObject,该属性在视图之间传递)

请注意
ContentView
如何拥有
[Goal]
,然后后续子视图将其作为
@Binding
获取-
$
符号用于通过参数传递该绑定:

struct Goal: Identifiable {
    var id: UUID
    var type: String // type of goals
    var name: String // the custom name of the goal
    var isLongTerm: Bool // if goal is a long term goal (no deadline)
    var progress: [Progress] // an array of progress for each day
    
    init(_ t:String, _ n:String, _ iLT: Bool, _ p: [Progress]) {
            id = UUID()
            type = t
            name = n
            isLongTerm = iLT
            progress = p
        }
}

struct ContentView: View {
    @State var goals: [Goal] = []

    var body: some View {
        TabView{
            VStack{
                Text("You have")
                Text("\(goals.count)")
                Text("tasks to do")
            }.tabItem { Text("Home")}
            MyScroll(numRange: 1..<100, goals: $goals).tabItem { Text("My Goals") }
        }
    }
}

struct MyScroll: View {
    var numRange: Range<Int>
    @Binding var goals: [Goal]
    
    var body: some View {
        NavigationView{
            VStack{
                NavigationLink(destination: AddView(goals:$goals)){
                    Image(systemName: "folder.badge.plus")
                }
                List(goals) { goal in
                    HStack(alignment: .center){
                        Text(goal.name)
                    }
                }
            }
        }.navigationTitle(Text("1111"))
    }
}

struct AddView: View {
    @Binding var goals:[Goal]
    @State var types = ["study", "workout", "hobby", "habbit"]
    @State private var selected = false
    @State var selection = Set<String>()
    @State var goalName: String = ""
    @State var goalType: String = ""
    @State var isLongTerm: Bool = false
    @State var progress: [Progress] = []
    
    var body: some View {
        VStack{
            Text("Create your goal")
            // type in name
            HStack{
                TextField("Name", text: $goalName)
            }.padding()
            // choose type: a selection list
            HStack{
                List(types, id: \.self, selection: $selection) {
                    Text($0)
                }
                .navigationBarItems(trailing: EditButton())
            }.padding()
            // toggle if it is a logn term goal
            HStack{
                Toggle(isOn: $selected) {
                    Text("Is your goal Long Term (no end date)")
                }.padding()
            }.padding()
            Button(action: {
                addGoal(goalType, goalName, isLongTerm, progress)
            }, label: {
                /*@START_MENU_TOKEN@*/Text("Button")/*@END_MENU_TOKEN@*/
            })
        }
    }
        
    // function that add the goal instance to the goals
    func addGoal( _ t:String, _ n:String, _ iLT: Bool, _ p: [Progress]){
        let item: Goal = Goal(t,n,iLT,[])
        goals.append(item)
    }
}
struct目标:可识别{
变量id:UUID
变量类型:字符串//目标类型
var name:String//目标的自定义名称
var isLongTerm:Bool//如果目标是长期目标(无最后期限)
var progress:[进度]//每天的进度数组
init(ut:String,n:String,iLT:Bool,p:[进度]){
id=UUID()
类型=t
name=n
isLongTerm=iLT
进度=p
}
}
结构ContentView:View{
@状态变量目标:[目标]=[]
var body:一些观点{
TabView{
VStack{
文本(“您有”)
文本(“\(goals.count)”)
文本(“要做的任务”)
}.tabItem{Text(“Home”)}

MyScroll(numRange:1..你需要@State-
@State var goals:[Goal]=[]
你能显示目标类型的代码吗?我想你忘了给我们显示它了。我尝试添加@State,但它不起作用,我刚刚更新了GoalThank u的代码。这是一个非常有用和仔细的解释!
struct Goal: Identifiable {
    var id: UUID
    var type: String // type of goals
    var name: String // the custom name of the goal
    var isLongTerm: Bool // if goal is a long term goal (no deadline)
    var progress: [Progress] // an array of progress for each day
    
    init(_ t:String, _ n:String, _ iLT: Bool, _ p: [Progress]) {
            id = UUID()
            type = t
            name = n
            isLongTerm = iLT
            progress = p
        }
}

struct ContentView: View {
    @State var goals: [Goal] = []

    var body: some View {
        TabView{
            VStack{
                Text("You have")
                Text("\(goals.count)")
                Text("tasks to do")
            }.tabItem { Text("Home")}
            MyScroll(numRange: 1..<100, goals: $goals).tabItem { Text("My Goals") }
        }
    }
}

struct MyScroll: View {
    var numRange: Range<Int>
    @Binding var goals: [Goal]
    
    var body: some View {
        NavigationView{
            VStack{
                NavigationLink(destination: AddView(goals:$goals)){
                    Image(systemName: "folder.badge.plus")
                }
                List(goals) { goal in
                    HStack(alignment: .center){
                        Text(goal.name)
                    }
                }
            }
        }.navigationTitle(Text("1111"))
    }
}

struct AddView: View {
    @Binding var goals:[Goal]
    @State var types = ["study", "workout", "hobby", "habbit"]
    @State private var selected = false
    @State var selection = Set<String>()
    @State var goalName: String = ""
    @State var goalType: String = ""
    @State var isLongTerm: Bool = false
    @State var progress: [Progress] = []
    
    var body: some View {
        VStack{
            Text("Create your goal")
            // type in name
            HStack{
                TextField("Name", text: $goalName)
            }.padding()
            // choose type: a selection list
            HStack{
                List(types, id: \.self, selection: $selection) {
                    Text($0)
                }
                .navigationBarItems(trailing: EditButton())
            }.padding()
            // toggle if it is a logn term goal
            HStack{
                Toggle(isOn: $selected) {
                    Text("Is your goal Long Term (no end date)")
                }.padding()
            }.padding()
            Button(action: {
                addGoal(goalType, goalName, isLongTerm, progress)
            }, label: {
                /*@START_MENU_TOKEN@*/Text("Button")/*@END_MENU_TOKEN@*/
            })
        }
    }
        
    // function that add the goal instance to the goals
    func addGoal( _ t:String, _ n:String, _ iLT: Bool, _ p: [Progress]){
        let item: Goal = Goal(t,n,iLT,[])
        goals.append(item)
    }
}