skip to Main Content

I’m kind of lost on how to work with states with Dynamic forms.
I surely cannot create states for each Field because I don’t know how many fields are there as these Forms are basically built from a JSON Response.

Here is basically what I have right now which is what I’m looking to change.
Initially I created a state for each field back when the forms were not built dynamically and now I’m stuck on how to proceed.

I thought about using Dictionary but I ain’t sure how good of a solution is that.

    @State var textfieldText: String = ""
    @State var SKU: String = ""
    @State private var showScanner: Bool = false
    var currentForm: FormModel
    @State var RecordDate: Date = Date.now
    @State var Formresponse: [String: Any] = [:]//This one is set to any because the value can vary from a string to [] to even a whole object

How I’m Rendering my form :

          ForEach(currentForm.item, id:.idf) { it in
                
                        if (!it.questionItem.hidden)
                        {
                            switch it.questionItem.questionType {
                        case .dateQuestion :
                                    DateField(title: it.title, currentDate: $RecordDate)
                        case .choiceQuestion:
                            Text("choice question")
                        case .scannerQuestion:
                            ScannerField(title: it.title, SKU: $SKU, showScanner: $showScanner)
                            
                        case .textQuestion:
                            TextQuestionField(title: it.title, email: currentForm.owner, text: $textfieldText)
                            }
                        }
                    
                }

I’ll eventually have to submit this data in a form of dictionary which is why I thought about using a Dict ["fieldID":"FieldInput","fieldId2":"FieldInput2"..]

2

Answers


  1. I think you only need one State and that is for the formResponse. You can pass that as a Binding to each input field view and within that view you can create a custom Binding to get and set the answer to the formResponse. Something like this:

    struct FormFieldInputView: View {
        @Binding var formResponse: [String: Any]
        let field: String
    
        var body: some View {
            TextField(field, text: Binding(
                get: {
                    formResponse[field] as? String ?? ""
                },
                set: { newValue in
                    formResponse[field] = newValue
                })
            )
        }
    }
    
    Login or Signup to reply.
  2. define enum for the questions type:

    enum FormItemType {
       case dataQuestion
       case choiceQuestion
       case scannerQuestion
       case textQuestion
    }
    

    define the item model for the questions type:

    struct FormItemModel : Identifiable {
        var type : FormItemType
        var itemObject : Any
        var userInput : String?
        let id : UUID
    }
    

    define the form view model:

    final class FormModel : ObservableObject {        
        @Published var items : [FormItemModel] = []
    }
    

    and the view :

    struct ContentView: View {
        
        @ObservedObject var formViewModel: FormModel
        
        @Binding var currentInput : String
        
        var body: some View {
            
            List {
                ForEach(formViewModel.items, id: .id, content: { item in
                    
                    switch item.type {
                    case .dataQuestion:
                        Text(item.itemObject as? String ?? "")
                        
                    case .scannerQuestion:
                        Text("(item.itemObject as? Int ?? 0 )")
                        
                    case .choiceQuestion:
                        if let dic = item.itemObject as? [String:String]{
                            VStack{
                                Text(dic["Q"]!)
                                Text(dic["A1"]!)
                                Text(dic["A2"]!)
                                Text(dic["A3"]!)
                            }
                        }
                        
                    case .textQuestion:
                        
                        VStack{
                            Text(item.itemObject as? String ?? "")
                            TextEditor(text: $currentInput)
                        }
                    }
                })//ForEach
            }//List
        }
    }//View
    

    and here’s the dummy values for the form:

    items = [FormItemModel(type: .textQuestion, itemObject: "Tell Me About Yourself...", id: UUID()),
                 FormItemModel(type: .choiceQuestion,
                               itemObject: ["Q":"How much is 1+1?", "A1":"1", "A2":"2", "A3":"3"],
                               id: UUID()),
                 FormItemModel(type: .scannerQuestion, itemObject: 1110111011, id: UUID())
        ]
    

    and the result:
    enter image description here

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search