【问题标题】:How to mutate the variable that passed from other views如何改变从其他视图传递的变量
【发布时间】:2021-03-19 14:37:27
【问题描述】:

我是 SwiftUI 的新手,我尝试创建一个应用程序,它有一个目标列表,在列表上方,有一个添加按钮用于添加目标并将其显示在列表中。目前,我无法将目标实例添加到目标(目标数组)中,在创建视图中,我尝试将新的目标实例附加到我在另一个视图中创建的目标中。它给了我一条错误消息:Cannot use mutating member on immutable value: 'self' is immutablegoals.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)
    }
}

目标只是我为存储信息而创建的结构:

import Foundation

// This is the structure for each goal when it is created
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
    }
}

【问题讨论】:

  • 你需要@State - @State var goals: [Goal] = []
  • 你能显示目标类型的代码吗?我想你忘了给我们看。
  • 我尝试添加@State但它不起作用,我刚刚更新了Goal的代码

标签: swift swiftui


【解决方案1】:

解决此问题的一种方法是使用@Binding 将@State 保存在父视图中,并通过视图层次结构向下传递,让子级发送数据备份。

(需要注意的是,在当前版本的 SwiftUI 中,通过多个视图发送 Binding 看起来可能会产生意想不到的结果,但一两个级别似乎没问题。另一种选择是使用带有 @Published 属性的 ObservableObject在视图之间传递)

注意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)
    }
}

您的 addGoal 函数不再需要改变,因为它实际上不再改变自己的状态(无论如何这在 SwiftUI 中不起作用)。

作为旁注,我会谨慎地编写您的初始化程序和函数,就像您使用 _ 未命名参数所做的那样——我在您的原始代码中发现了一个您打算传递目标,而是传递该参数的类型,并且由于所有参数都未命名,因此没有警告。

【讨论】:

  • 非常感谢!这是一个非常有用和仔细的解释!
猜你喜欢
  • 1970-01-01
  • 2021-05-22
  • 1970-01-01
  • 1970-01-01
  • 2013-06-03
  • 2018-02-21
  • 2019-03-17
  • 1970-01-01
  • 2022-01-15
相关资源
最近更新 更多