Create @State vars dynamically

Hi,

I have a form that will have a number of TextFields depending on the data that is passed in. As i need to record what has been entered into these TextFields, i need state vars.

The content is created dynamically but i also need to create the State Vars.

Could someone please explain how this would be done.

This is my code so far.


import SwiftUI

struct CandidateReview: View {
    var reviewScore = ["Terrible", "Poor", "Average", "Good", "Excellent"]
    var reviewCriterias = ["Communication", "Time Management", "Work Ethic", "Creativity", "Teamwork", "General"]
    
    var candidateName = "John Doe"
    
    @State private var selectedReviewScore = "Average"
    @State private var scoreNotes = ""
   
    
    var body: some View {
        NavigationStack{
           
            Form{
                ForEach(reviewCriterias, id: \.self) { reviewCriteria in
                    Section(header: Text(reviewCriteria)){
                        Picker("\(reviewCriteria) Score", selection: self.$selectedReviewScore){
                            ForEach(reviewScore, id: \.self){
                                Text($0)
                            }
                        }
                        NavigationLink(destination:
                            NavigationView {
                                Form {
                                    TextField(text: $scoreNotes, label: {})
                                }.navigationTitle("\(reviewCriteria) review notes")
                            }
                        ){
                           Text("This is the text that is shown")
                        }
                    }
                }
            }
            .navigationTitle(candidateName)
        }
        
    }
}

#Preview {
    CandidateReview()
}

you need to learn @Binding for that. You would create the data in the model that you want to show in the text fields and then bind the fields to that data. There is no @State in this case. Sometimes the UI controls have slightly different types to your model property types in which case you can use computed bindings to do the transformation. The same way you would use computed vars to transform readonly model or state data when passing it down to child Views.

Hi @Adamhumbug , I'd recommend using an Observable class to store an array containing your review criteria (as a Struct with many properties).

The struct can contain the score, the text and the title of the review, and each one can be updated without the others being updated. Something like this:

enum ReviewScore: String, CaseIterable {
    case terrible, poor, average, good, excellent
}

struct ReviewCriteria: Identifiable, Equatable {
    var id = UUID()
    var title: String
    var text: String = ""
    var score: ReviewScore = .average
    init(title: String, text: String = "", score: ReviewScore? = .average) {
        self.title = title
        self.text = text
        self.score = .average
    }
}

@Observable
final class Reviews {
    var reviewCriterias: [ReviewCriteria] = [ReviewCriteria(title: "Communication"), ReviewCriteria(title: "Time Management"), ReviewCriteria(title: "Work Ethic"), ReviewCriteria(title: "Creativity"), ReviewCriteria(title: "Teamwork"), ReviewCriteria(title: "General")]
}

Where you could then implement it in your code like this:

struct ContentView: View {
 @State private var reviews: Reviews = Reviews()
    var candidateName = "John Doe"
    
    var body: some View {
        NavigationStack {
            Form {
                ForEach($reviews.reviewCriterias) { $reviewCriteria in
                    Section(header: Text(reviewCriteria.title)){
                        Picker("\(reviewCriteria.score) Score", selection: $reviewCriteria.score){
                            ForEach(ReviewScore.allCases, id: \.rawValue){ score in
                                Text(score.rawValue)
                                    .tag(score)
                            }
                        }
                        NavigationLink(destination:
                            Form {
                                TextField(text: $reviewCriteria.text, label: {})
                            }
                            .navigationTitle("\(reviewCriteria.title) review notes")
                        )
                        {
                            Text("This is the text that is shown")
                        }
                    }
                }
                
                .navigationTitle(candidateName)
            }
        }
        
    }
}

This would allow you to keep the criteria and the text field bundled together to access the data later as well. Let me know if I've understood your question correctly!

Best,

Sydney

Create @State vars dynamically
 
 
Q