You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI开发报错解决:跨视图传递的数组变量无法修改,添加Goal实例失败

Hey there! Let's fix this issue step by step — the problem here boils down to how SwiftUI handles state and value types, which is a common gotcha for new SwiftUI developers.

What's Causing the Error?

SwiftUI views are value types (structs), so when you pass your goals array to AddView, you're passing a copy of the array, not a reference. Plus, regular var properties in view structs are immutable by default (you can't mutate self in a view's body or methods without using SwiftUI's state wrappers). The mutating modifier won't work here because view structs are designed to be immutable except for state-managed properties.

Step-by-Step Fix

We'll use SwiftUI's state management tools (@State and @Binding) to let AddView modify the original goals array in MyScroll.

1. Update MyScroll to Manage State

First, we'll turn goals into a @State property in MyScroll — this tells SwiftUI to track changes to the array and refresh the view when it updates.

struct MyScroll: View {
    var numRange: Range<Int>
    // Use @State to manage the mutable goals array
    @State private var goals: [Goal] = []
    
    init(_ r: Range<Int>) {
        numRange = r
        // No need to initialize goals here — @State provides a default empty array
    }
    
    var body: some View {
        NavigationView{
            VStack{
                // Pass a binding to AddView using $goals
                NavigationLink(destination: AddView(goals: $goals)){
                    Image(systemName: "folder.badge.plus")
                }
                List(goals) { goal in
                    HStack(alignment: .center){
                        Text(goal.name)
                    }
                }
            }
            .navigationTitle("My Goals") // Clean up navigation title syntax
        }
    }
}

2. Modify AddView to Use Binding

Change goals to a @Binding property in AddView — this lets the view reference and modify the original goals array from MyScroll instead of a copy. We'll also fix the type selection logic (since you probably want a single type, not multiple) and add guard clauses to prevent invalid goals.

struct AddView: View {
    // Use @Binding to connect to the parent's goals array
    @Binding var goals: [Goal]
    @State var types = ["study", "workout", "hobby", "habbit"]
    @State private var selected = false
    // Switch to single selection for goal type (more logical for this use case)
    @State var selection: String?
    @State var goalName: String = ""
    @State var isLongTerm: Bool = false
    @State var progress: [Progress] = []
    
    var body: some View {
        VStack{
            Text("Create your goal")
            // Goal name input
            HStack{
                TextField("Name", text: $goalName)
            }.padding()
            // Goal type selection
            HStack{
                List(types, id: \.self, selection: $selection) {
                    Text($0)
                }
                .navigationBarItems(trailing: EditButton())
            }.padding()
            // Long-term goal toggle
            HStack{
                Toggle(isOn: $selected) {
                    Text("Is your goal Long Term (no end date)")
                }.padding()
            }.padding()
            // Create goal button (disabled if inputs are invalid)
            Button(action: addGoal, label: {
                Text("Create Goal")
            })
            .disabled(goalName.isEmpty || selection == nil)
        }
        .navigationTitle("Add Goal")
    }
    
    // Remove mutating — @Binding lets us modify the parent's array directly
    func addGoal(){
        // Guard against empty inputs
        guard let goalType = selection, !goalName.isEmpty else {
            return
        }
        let newGoal = Goal(goalType, goalName, selected, [])
        goals.append(newGoal)
        // Reset inputs for next use
        goalName = ""
        selection = nil
        selected = false
    }
}

3. Clean Up ContentView

The original goals variable in ContentView is unused, so we can remove it:

struct ContentView: View {
    var body: some View {
        TabView{
            VStack{
                Text("You have")
                Text("0") // To show real count, move @State goals to ContentView and pass it here too
                Text("tasks to do")
            }.tabItem { Text("Home")}
            MyScroll(1..<100).tabItem { Text("My Goals") }
        }
    }
}

Bonus: Show Real Goal Count in Home Tab

If you want the Home tab to display the actual number of goals, move the @State goals property up to ContentView and pass bindings to both tabs:

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

// Update MyScroll to accept a binding from ContentView
struct MyScroll: View {
    var numRange: Range<Int>
    @Binding var goals: [Goal]
    
    init(_ r: Range<Int>, goals: Binding<[Goal]>) {
        numRange = r
        self._goals = goals
    }
    
    // Rest of MyScroll code stays the same
}

内容的提问来源于stack exchange,提问作者siiulan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 11:17:30