How to clear variable when triggered in another view - swift

I am attempting to clear variable values when a button is pressed in another view. Code below:
Root view
struct RecipeEditorHomeMenu: View {
#Environment(\.dismiss) var dismiss
#State var recipeAddedSuccess = false
#State private var showSaveButton = false
#State var showSuccessMessage = false
#State private var sheetMode: SheetMode = .none
#ObservedObject var recipeClass = Recipe()
var onDismiss: (() -> Void)?
var body: some View {
GeometryReader{ geo in
VStack{
if showSuccessMessage {
RecipeSuccessPopUp(shown: $showSuccessMessage, onDismiss: onDismiss)
}
RecipeEditorView(showSuccessMessage: $showSuccessMessage)
.blur(radius: showSuccessMessage ? 15 : 0)
.padding(.top, 80)
// Spacer()
//display save button
FlexibleSheet(sheetMode: $sheetMode) {
SaveRecipeButton(showSuccessMessage: $showSuccessMessage)
//sets coordinates of view on dash
.offset(y:-200)
}
}
.onAppear{
recipeAddedSuccess = false
}
//center view
.alignmentGuide(VerticalAlignment.center, computeValue: { $0[.bottom] })
.position(x: geo.size.width / 2, y: geo.size.height / 2)
.environmentObject(recipeClass)
}
}
}
EditorView (where I would like to clear variables and refresh the view so now it appears new and no variables have values anymore:
struct RecipeEditorView: View {
#EnvironmentObject var recipeClass: Recipe
#State var recipeTitle = ""
#State private var recipeTime = "Cook Time"
#State private var pickerTime: String = ""
//calls macro pickers#State var proteinPicker: Int = 0
#Binding var showSuccessMessage: Bool
var cookingTime = ["5 Mins", "10 Mins","15 Mins","20 Mins","25 Mins","30 Mins ","45 Mins ","1 Hour","2 Hours", "A Long Time", "A Very Long Time"]
var resetPickerTime: (() -> Void)?
var body: some View {
VStack{
TextField("Recipe Title", text: $recipeTitle)
.onChange(of: recipeTitle, perform: { _ in
recipeClass.recipeTitle = recipeTitle
})
.foregroundColor(Color.black)
.font(.title3)
.multilineTextAlignment(.center)
//macro selectors
HStack{
Picker(selection: $carbPicker, label: Text("")) {
ForEach(pickerGramCounter(), id: \.self) {
Text(String($0) + "g Carbs")
}
.onChange(of: carbPicker, perform: { _ in
recipeClass.recipeCarbMacro = carbPicker
})
}
.accentColor(.gray)
}
.accentColor(.gray)
}
HStack(spacing: 0){
ZStack{
Image(systemName:("clock"))
.padding(.leading, 150)
.foregroundColor(Color("completeGreen"))
Picker(selection: $pickerTime, label: Text("Gender")) {
ForEach(cookingTime, id: \.self) {
Text($0)
}
}
.onChange(of: pickerTime, perform: { _ in
recipeClass.recipePrepTime = pickerTime
})
.accentColor(.gray)
}
.multilineTextAlignment(.center)
}
}
}
}
Button where I save the recipes (and would like to clear the variables here:
struct SaveRecipeButton: View {
#Binding var showSuccessMessage: Bool
//stores recipe
#EnvironmentObject var recipeClass: Recipe
#State var isNewRecipeValid = false
static var newRecipeCreated = false
var body: some View {
Button(action: {
//action
}){
HStack{
Image(systemName: "pencil").resizable()
.frame(width:40, height:40)
.foregroundColor(.white)
Button(action: {
if (recipeClass.recipeTitle != "" &&
recipeClass.recipePrepTime != "" &&
){
isNewRecipeValid = true
showSuccessMessage = true
saveRecipe()
}
else{
showSuccessMessage = false
isNewRecipeValid = false
}
}){
Text("Save Recipe")
.font(.title)
.frame(width:200)
.foregroundColor(.white)
}
}
.padding(EdgeInsets(top: 12, leading: 100, bottom: 12, trailing: 100))
.background(
RoundedRectangle(cornerRadius: 10)
.fill(
Color("completeGreen")))
}
.transition(.sideSlide)
.animation(.easeInOut(duration: 0.25))
}
}
My recipe class holds each variable as #Published variables. I tried clearing the variables in this class as well, but the values don't refresh in the view (example: If I have a recipeTitle as "Eggs" and save that recipe down, recipeTitle does not update in the view)
**update recipe class:
class Recipe: ObservableObject, Identifiable {
let id = UUID().uuidString
#Published var recipeTitle: String = ""
#Published var isCompleted = false
#Published var recipeCaloriesMacro: Int = 0
#Published var recipeFatMacro: Int = 0
#Published var recipeProteinMacro: Int = 0
}

I asume you want to reset the values in Recipe. The reason because this doesn´t reflect into RecipeEditorView are the #State values in your RecipeEditorView.
You are neglecting a basic principle of SwiftUI: "Single source of truth".
RecipeEditorView has several #State values that you then bind to your Pickers and TextFields. Then you observe their changes and synchronize with your ViewModel Recipe.
Don´t do that, use the ViewModel itself as the (I know I´m repeating myself here) "Single source of truth".
Another issue. If a view manages the lifecycle of an ObservableObject like instantiating it and holding on to it, declare it as #StateObject. #ObservedObject is only needed it the class is injected and managed up the hierarchy.
In RecipeEditorHomeMenu:
#StateObject var recipeClass = Recipe()
in RecipeEditorView remove all that #State vars that are used to define the recipe e.g.: recipeTitle, carbPicker and pickerTime and bind to the values in the ViewModel.
Possible implementation for recipeTitle:
// this needs to be changed
TextField("Recipe Title", text: $recipeClass.recipeTitle)
Do this for the others too.
Now in SaveRecipeButton you can change these vars to whatever value you want. E.g.: resetting the title would look like:
recipeClass.recipeTitle = ""

Related

Textfield toggling Bool when clicked

I have a Bool value titled editMode which determines if the user can edit information on a current page. When this is toggled true and I enter a textfield, the Bool is flipped back to false. This only occurs on the first click into the textfield. On subsequent clicks into the textfield, it does not toggle (working fine)
How can I fix this issue?
struct RecipeControllerModal: View {
#Environment(\.dismiss) var dismiss // << dismiss view
#ObservedObject var rm = RecipeLogic()
#ObservedObject var ema = EditModeActive() // calling in next view
var body: some View {
NavigationView{
VStack{
VStack{
RecipeDashHeader(ema: ema) // <<passing editMode
.padding()
}
}
}
RecipeDashHeader // <<textfield view
struct RecipeDashHeader: View {
#State var recipeName = ""
#ObservedObject var ema: EditModeActive
#ViewBuilder
var body: some View {
if ema.editMode {
VStack{
TextField(recipeName, text: $recipeName)
.foregroundColor(!ema.editMode ? Color.black : Color.red)
.font(.title2)
.multilineTextAlignment(.center)
.padding()
.onChange(of: recipeName, perform: { _ in
ema.recipeTitle = recipeName
})
}
Published EditMode Variable
class EditModeActive: ObservableObject {
#Published var editMode: Bool = false
#Published var recipeTitle = ""
}
Try this approach using the ema.recipeTitle directly in your TextField and
using #StateObject var ema as mentioned in the comments.
Here is the example code I used in my tests:
struct ContentView: View {
var body: some View {
RecipeControllerModal()
}
}
struct RecipeControllerModal: View {
#Environment(\.dismiss) var dismiss // << dismiss view
// #ObservedObject var rm = RecipeLogic() // for testing
#StateObject var ema = EditModeActive() // <-- here
var body: some View {
NavigationView{ // <-- here should use NavigationStack
VStack{
VStack{
RecipeDashHeader(ema: ema) // <<passing editMode
.padding()
}
// for testing
Button(ema.editMode ? "save" : "edit") {
ema.editMode.toggle()
}
}
}
}
}
struct RecipeDashHeader: View {
// #State var recipeName = "" // <-- here not needed
#ObservedObject var ema: EditModeActive
var body: some View {
if ema.editMode {
VStack{
TextField(ema.recipeTitle, text: $ema.recipeTitle) // <-- here
.foregroundColor(!ema.editMode ? Color.black : Color.red) // <--- usesless here ema.editMode == true always
.font(.title2)
.multilineTextAlignment(.center)
.padding()
.border(.red)
// not needed
// .onChange(of: ema.recipeTitle, perform: { name in
// ema.recipeTitle = name
// })
}
} else {
Text(ema.recipeTitle)
}
}
}
class EditModeActive: ObservableObject {
#Published var editMode: Bool = false
#Published var recipeTitle: String = "recipe title" // <-- here
}

Conditional view modifier sometimes doesn't want to update

As I am working on a study app, I'm tring to build a set of cards, that a user can swipe each individual card, in this case a view, in a foreach loop and when flipped through all of them, it resets the cards to normal stack. The program works but sometimes the stack of cards doesn't reset. Each individual card updates a variable in a viewModel which my conditional view modifier looks at, to reset the stack of cards using offset and when condition is satisfied, the card view updates, while using ".onChange" to look for the change in the viewModel to then update the variable back to original state.
I've printed each variable at each step of the way and every variable updates and I can only assume that the way I'm updating my view, using conditional view modifier, may not be the correct way to go about. Any suggestions will be appreciated.
Here is my code:
The view that houses the card views with the conditional view modifier
extension View {
#ViewBuilder func `resetCards`<Content: View>(_ condition: Bool, transform: (Self) -> Content) -> some View {
if condition == true {
transform(self).offset(x: 0, y: 0)
} else {
self
}
}
}
struct StudyListView: View {
#ObservedObject var currentStudySet: HomeViewModel
#ObservedObject var studyCards: StudyListViewModel = StudyListViewModel()
#State var studyItem: StudyModel
#State var index: Int
var body: some View {
ForEach(currentStudySet.allSets[index].studyItem.reversed()) { item in
StudyCardItemView(currentCard: studyCards, card: item, count: currentStudySet.allSets[index].studyItem.count)
.resetCards(studyCards.isDone) { view in
view
}
.onChange(of: studyCards.isDone, perform: { _ in
studyCards.isDone = false
})
}
}
}
StudyCardItemView
struct StudyCardItemView: View {
#StateObject var currentCard: StudyListViewModel
#State var card: StudyItemModel
#State var count: Int
#State var offset = CGSize.zero
var body: some View {
VStack{
VStack{
ZStack(alignment: .center){
Text("\(card.itemTitle)")
}
}
}
.frame(width: 350, height: 200)
.background(Color.white)
.cornerRadius(10)
.shadow(radius: 5)
.padding(5)
.rotationEffect(.degrees(Double(offset.width / 5)))
.offset(x: offset.width * 5, y: 0)
.gesture(
DragGesture()
.onChanged { gesture in
offset = gesture.translation
}
.onEnded{ _ in
if abs(offset.width) > 100 {
currentCard.cardsSortedThrough += 1
if (currentCard.cardsSortedThrough == count) {
currentCard.isDone = true
currentCard.cardsSortedThrough = 0
}
} else {
offset = .zero
}
}
)
}
}
HomeViewModel
class HomeViewModel: ObservableObject {
#Published var studySet: StudyModel = StudyModel()
#Published var allSets: [StudyModel] = [StudyModel()]
}
I initialize allSets with one StudyModel() to see it in the preview
StudyListViewModel
class StudyListViewModel: ObservableObject {
#Published var cardsSortedThrough: Int = 0
#Published var isDone: Bool = false
}
StudyModel
import SwiftUI
struct StudyModel: Hashable{
var title: String = ""
var days = ["One day", "Two days", "Three days", "Four days", "Five days", "Six days", "Seven days"]
var studyGoals = "One day"
var studyItem: [StudyItemModel] = []
}
Lastly, StudyItemModel
struct StudyItemModel: Hashable, Identifiable{
let id = UUID()
var itemTitle: String = ""
var itemDescription: String = ""
}
Once again, any help would be appreciated, thanks in advance!
I just found a fix and I put .onChange at the end for StudyCardItemView. Basically, the onChange helps the view scan for a change in currentCard.isDone variable every time it was called in the foreach loop and updates offset individuality. This made my conditional view modifier obsolete and just use the onChange to check for the condition.
I still used onChange outside the view with the foreach loop, just to set currentCard.isDone variable false because the variable will be set after all array elements are iterator through.
The updated code:
StudyCardItemView
struct StudyCardItemView: View {
#StateObject var currentCard: StudyListViewModel
#State var card: StudyItemModel
#State var count: Int
#State var offset = CGSize.zero
var body: some View {
VStack{
VStack{
ZStack(alignment: .center){
Text("\(card.itemTitle)")
}
}
}
.frame(width: 350, height: 200)
.background(Color.white)
.cornerRadius(10)
.shadow(radius: 5)
.padding(5)
.rotationEffect(.degrees(Double(offset.width / 5)))
.offset(x: offset.width * 5, y: 0)
.gesture(
DragGesture()
.onChanged { gesture in
offset = gesture.translation
}
.onEnded{ _ in
if abs(offset.width) > 100 {
currentCard.cardsSortedThrough += 1
if (currentCard.cardsSortedThrough == count) {
currentCard.isDone = true
currentCard.cardsSortedThrough = 0
}
} else {
offset = .zero
}
}
)
.onChange(of: currentCard.isDone, perform: {_ in
offset = .zero
})
}
}
StudyListView
struct StudyListView: View {
#ObservedObject var currentStudySet: HomeViewModel
#ObservedObject var studyCards: StudyListViewModel = StudyListViewModel()
#State var studyItem: StudyModel
#State var index: Int
var body: some View {
ForEach(currentStudySet.allSets[index].studyItem.reversed()) { item in
StudyCardItemView(currentCard: studyCards, card: item, count:
currentStudySet.allSets[index].studyItem.count)
.onChange(of: studyCards.isDone, perform: { _ in
studyCards.isDone = false
})
}
}
}
Hope this helps anyone in the future!

SwiftUI: Why do I get the same pattern of random items in an array?

I'm working on my project and there's a section where you can test yourself on Japanese letters. You tap a button that plays a sound of a letter then you choose the right button out of three with the correct letter. there are 10 questions in total and it should always randomize, the letters but whenever you come back to the view, the first question is always the same. after the first one it randomizes the rest of the questions but the first question always has the same pattern of letters. What I want is getting a random pattern of letters for the first question every time you come back to the view. I'd appreciate any help.
here's the code of QuestionView:
import SwiftUI
struct HiraganaQuiz: View {
var hiraganas: [Japanese] = Bundle.main.decode("Hiragana.json")
#State private var correctAnswer = Int.random(in: 0...45)
#StateObject var soundplayer = Audio()
#State private var answer = ""
#State private var counter = 0
#State private var correctAnswerCounter = 0
#State private var showingAlert = false
#State private var alertMessage = ""
#State private var disabled = false
var body: some View {
ZStack {
Color.darkBackground
.ignoresSafeArea()
VStack {
Text("\(counter) / 10")
.padding(.top,40)
.foregroundColor(.white)
.font(.system(size:30).bold())
Text("Tap the speaker and choose the right letter")
Button {
soundplayer.playSounds(file: hiraganas[correctAnswer].voice1)
} label: {
Image(systemName: "speaker.wave.3.fill")
}
.font(.system(size:70))
height: 110)
HStack {
ForEach (0...2, id: \.self) { index in
Button {
letterTapped(index)
} label: {
Text(hiraganas[index].letter)
}
}
.disabled(disabe())
.foregroundColor(.white)
.font(.system(size: 35).bold())
Text("\(answer)")
.foregroundColor(.white)
.padding(.bottom,20)
.font(.system(size: 30))
Button {
resetTheGame()
} label: {
Text("Next")
}.buttonStyle(.plain)
.font(.system(size: 30).bold())
.frame(width: 200, height: 50)
}
}
.alert("⭐️ Well done ⭐️", isPresented: $showingAlert) {
Button("Retry", action: reset)
} message: {
Text(alertMessage)
}
} .onAppear { hiraganas = Bundle.main.decode("Hiragana.json").shuffled() }
}
I'm surprised this even compiles -- it should be giving you an error about trying to initialize state like this.
Instead, you can shuffle in the properties in the property initializer:
struct HiraganaQuiz: View {
#State private var hiraganas: [Japanese] = Bundle.main.decode("Hiragana.json").shuffled()
Update, to show the request for onAppear usage:
struct HiraganaQuiz: View {
#State private var hiraganas: [Japanese] = []
var body: some View {
ZStack {
// body content
}.onAppear {
hiraganas = Bundle.main.decode("Hiragana.json").shuffled()
}
}
}

Turn Button into Picker

I have this code, where there are three buttons which have values stored in each of them.
When I press the Add button, the values add up and appear on the list. However, I am not able to check which values are being selected.
How can I make the button look like a picker, so that when the user clicks the button A, B, or C, it will show with a checkmark that it has been selected and when the user taps the Add button, the value of the selected button and "gp" should show up on the list? Also, the checkmark should disappear once the Add button is selected so that the user can select another list.
Such as:
If A and B are selected, the list should look like:
A = 2.0, B = 5.0, gp = 7.0.
If A and C are selected, the list should look like:
A= 2.0, C = 7.0, gp = 9.0.
I have tried using Picker and other methods, however, I couldn't get through. I have found this as the best solution. However, I am not able to put a checkmark on the buttons and not able to show the selected values on the list.
import SwiftUI
struct MainView: View {
#State var br = Double()
#State var loadpay = Double()
#State var gp : Double = 0
#State var count: Int = 1
#State var listcheck = Bool()
#StateObject var taskStore = TaskStore()
#State var a = 2.0
#State var b = 5.0
#State var c = 7.0
//var userCasual = UserDefaults.standard.value(forKey: "userCasual") as? String ?? ""
#State var name = String()
func addNewToDo() {
taskStore.tasks.append(Task(id: String(taskStore.tasks.count + 1), toDoItem: "load \(gp)", amount: Double(gp)))
self.gp = 0.0
}
func stepcount() {
count += 1
}
var body: some View {
HStack {
Button(action: { gp += a }) {
Text("A =").frame(width: 70, height: 15)
.foregroundColor(.yellow)
}
.background(RoundedRectangle(cornerRadius: 5))
.foregroundColor(.black)
Button(action: { gp += b }) {
Text("B =") .frame(width: 70, height: 15)
.foregroundColor(.yellow)
}
.background(RoundedRectangle(cornerRadius: 5))
.foregroundColor(.black)
Button(action: { gp += c }) {
Text("C =").frame(width: 70, height: 15)
.foregroundColor(.yellow)
}
.background(RoundedRectangle(cornerRadius: 5))
.foregroundColor(.black)
}
HStack(spacing: 15) {
Button(
String(format: ""),
action: {
print("pay for the shift is ")
gp += loadpay
}
)
Button(
action: {
addNewToDo()
stepcount()
},
label: { Text("Add")}
)
}
Form {
ForEach(self.taskStore.tasks) { task in
Text(task.toDoItem)
}
}
}
}
struct Task : Codable, Identifiable {
var id = ""
var toDoItem = ""
var amount = 0.0
}
class TaskStore : ObservableObject {
#Published var tasks = [Task]()
}
The issue is you are trying to turn a Button into something it is not. You can create your own view that responds to a tap and keeps its state so it knows whether it is currently selected or not. An example is this:
struct MultiPickerView: View {
#State private var selectA = false
#State private var selectB = false
#State private var selectC = false
let A = 2.0
let B = 5.0
let C = 7.0
var gp: Double {
(selectA ? A : 0) + (selectB ? B : 0) + (selectC ? C : 0)
}
var body: some View {
VStack {
HStack {
SelectionButton(title: "A", selection: $selectA)
SelectionButton(title: "B", selection: $selectB)
SelectionButton(title: "C", selection: $selectC)
}
.foregroundColor(.blue)
Text(gp.description)
.padding()
}
}
}
struct SelectionButton: View {
let title: String
#Binding var selection: Bool
var body: some View {
HStack {
Image(systemName: selection ? "checkmark.circle" : "circle")
Text(title)
}
.padding()
.overlay(
RoundedRectangle(cornerRadius: 15)
.stroke(Color.blue, lineWidth: 4)
)
.padding()
.onTapGesture {
selection.toggle()
}
}
}
You could make it more adaptable by using an Array that has a struct that keeps the value and selected state, and run it though a ForEach, but this is the basics of the logic.

How to mutate the variable that passed from other views

I am new to the SwiftUI, I try to create an app, it has a list of goals and above the list, there is an add button to add a goal and display it on the list. Currently, I am having trouble adding the goal instance into the goals(array of goals), in the create view, I try to append a new instance of Goal to the goals that I created in another view. And it gives me an error message: Cannot use mutating member on immutable value: 'self' is immutable on the line goals.append(Goal(...)) Does anyone know how to fix it? here is my code! Thank you so much!
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)
}
}
The Goal is just a structure that I created for storing information:
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
}
}
One way to to this is by using a #Binding to hold #State in a parent view and pass it down through the view hierarchy, letting the children send data back up.
(One caveat is that sending a Binding through many views looks like it may have unexpected results in the current version of SwiftUI, but one or two levels seems to be fine. Another option is using an ObservableObject with a #Published property that gets passed between views)
Note how the ContentView owns the [Goal] and then the subsequent child views get it as a #Binding -- the $ symbol is used to pass that Binding through the parameters:
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)
}
}
Your addGoal function no longer has to be mutating, since it's not actually mutating its own state any more (which doesn't work in SwiftUI anyway).
As a side note, I'd be cautious about writing your initializers and functions like you're doing with the _ unnamed parameters -- I found one in your original code where you meant to be passing the name of the goal but instead were passing the type for that parameter, and because all of the parameters were/are unnamed, there's no warning about it.