Get data from view after it gets added on screen using ForEach

I have created a custom view which contains a textview, a textfield and a picker with three items.

Now this is added by iterating an array using a foreach loop. These can be more than one based on the response from the server. Now I wish to get the data back from the textfield and the textfield. I tried to use Binding in the forEach loop but it is now receiving any update done in these views.

Answered by OOPer in 676572022

Thanks for showing the refactored code. It compiled fine. I needed to add enableButtonBool = true, but it was a great help to guess what you want to do. (I still do not understand some parts of the code, but it may be better to show what I guessed.)

I may be mistaking something, but you may want to do something like this:

import SwiftUI

struct ChecklistModel: Codable, Hashable {
    var description, remarks, status: String?
}

struct CheckListCard: View {
    @Binding var checkListData : ChecklistModel
    @State var statusList: [String] = []
    @Binding var enableButtonBool: Bool
    
    var body: some View {
        let currentStatus = Binding<String>(
            get: {checkListData.status ?? ""},
            set: {checkListData.status = $0})
        let currentRemark = Binding<String>(
            get: {checkListData.remarks ?? ""},
            set: {checkListData.remarks = $0})
        VStack{
            Text("Description").padding()
                .onAppear(){
                    addData()
                }
            if let description = checkListData.description {
                Text(description)
                    .padding()
            }
            Text("Status").padding()
            if checkListData.status != nil {
                Picker("Status", selection: currentStatus) {
                    ForEach(statusList, id: \.self){ status in
                        Text(status)
                    }
                }.pickerStyle(SegmentedPickerStyle())
                .padding()
                .disabled(!enableButtonBool)
            }
            Text("Remarks")
            if checkListData.remarks != nil {
                TextField("Remarks", text: currentRemark)
                    .disabled(!enableButtonBool)
                    .padding()
                    .background(Color(.white))
                    .cornerRadius(8)
                    .accentColor(.gray)
            } else {
                TextField("Remarks", text: currentRemark)
                    .disabled(!enableButtonBool)
                    .padding()
                    .background(Color(.white))
                    .cornerRadius(8)
            }
        }
        .padding()
        .background(Color("light_gray"))
        .foregroundColor(.black)
        .cornerRadius(8)
        .shadow(radius: 10)
        .padding()
    }
    
    func addData() {
        statusList.append("Yes")
        statusList.append("No")
        statusList.append("NA")
        if let status = checkListData.status {
            statusList.append(status) //<- Why this is needed?
        }
    }
}

struct CheckListSheet: View {
    @State var taskId: Int
    
    @State var checklistResponse: [ChecklistModel] = [ChecklistModel()]
    @State var enableButtonBool: Bool = false
    
    @State var statusList: [String] = []
    
    var body: some View {
        Text("Checklist Items")
            .padding()
            .font(.title)
        ScrollView {
            ForEach(checklistResponse.indices, id:\.self) { index in
                CheckListCard(checkListData: $checklistResponse[index],
                              enableButtonBool: $enableButtonBool)
            }
            if enableButtonBool {
                Button("Update") {
                    updateCheckList()
                }
            }
        }
        .onAppear(){
            getChecklists()
        }
    }
    
    func updateCheckList()  {
        print("this is the checklistresponse \(checklistResponse)")
    }
    
    func getChecklists()  {
        checklistResponse = [
            ChecklistModel(description: "ist item", remarks: nil, status: "Yes"),
            ChecklistModel(description: "2nd item", remarks: "Fine", status: "no")
        ]
        enableButtonBool = true
    }
}

added code details below

Below is the code that I have currently

struct CheckListCard: View {
   
  @Binding var checkListData : ChecklistModel
  @State var statusList: [String] = []
  @State var currentStatus : String = ""
  @State var currentRemark: String = ""
  @Binding var enableButtonBool: Bool
   
  var body: some View {
    VStack{
      Text("Description").padding()
        .onAppear(){
          addData()
        }
      if checkListData.description != nil{
        Text(checkListData.description!)
          .padding()
      }
      Text("Status").padding()
      if checkListData.status != nil {
        Picker("Status", selection: $currentStatus ) {
          ForEach(GeneralMethods().uniqueElementsFrom(array: statusList), id: \.self){ status in
            Text(status)
          }
        }.pickerStyle(SegmentedPickerStyle())
        .padding()
        .disabled(!enableButtonBool)
         
      }
      Text("Remarks")
      if checkListData.remarks != nil{
        TextField("Remarks", text: $currentRemark)
          .disabled(!enableButtonBool)
          .padding()
          .background(Color(.white))
          .cornerRadius(8)
          .accentColor(.gray)
           
      }else{
        TextField("Remarks", text: $currentRemark)
          .disabled(!enableButtonBool)
          .padding()
          .background(Color(.white))
          .cornerRadius(8)
           
      }
    }
struct CheckListSheet: View {
   
  @State var taskId: Int
  @State var pmTaskResponse : PmTaskResponse
  @State var checklistResponse: [ChecklistModel] = [ChecklistModel()]
  @State var enableButtonBool: Bool = false
  var body: some View {
     
    Text("Checklist Items")
      .padding()
      .font(.title)
     
    ScrollView{
       
      ForEach(checklistResponse, id:\.self){ checklist in
        CheckListCard(checkListData: Binding(get: {
          return checklist
        }, set: { (newValue) in
          checklistResponse.append(newValue)
        }) , enableButtonBool: $enableButtonBool)
      }
      if enableButtonBool{
        Button("Update"){
          updateCheckList()
        }.buttonStyle(MainButton())
      }
    }
    .onAppear(){
      getChecklists()
    }
     
  }
   

@OOPer I have attached the file for you to see. Let me know if you need anything else.

I have refactored the file and I am sending it again.

Accepted Answer

Thanks for showing the refactored code. It compiled fine. I needed to add enableButtonBool = true, but it was a great help to guess what you want to do. (I still do not understand some parts of the code, but it may be better to show what I guessed.)

I may be mistaking something, but you may want to do something like this:

import SwiftUI

struct ChecklistModel: Codable, Hashable {
    var description, remarks, status: String?
}

struct CheckListCard: View {
    @Binding var checkListData : ChecklistModel
    @State var statusList: [String] = []
    @Binding var enableButtonBool: Bool
    
    var body: some View {
        let currentStatus = Binding<String>(
            get: {checkListData.status ?? ""},
            set: {checkListData.status = $0})
        let currentRemark = Binding<String>(
            get: {checkListData.remarks ?? ""},
            set: {checkListData.remarks = $0})
        VStack{
            Text("Description").padding()
                .onAppear(){
                    addData()
                }
            if let description = checkListData.description {
                Text(description)
                    .padding()
            }
            Text("Status").padding()
            if checkListData.status != nil {
                Picker("Status", selection: currentStatus) {
                    ForEach(statusList, id: \.self){ status in
                        Text(status)
                    }
                }.pickerStyle(SegmentedPickerStyle())
                .padding()
                .disabled(!enableButtonBool)
            }
            Text("Remarks")
            if checkListData.remarks != nil {
                TextField("Remarks", text: currentRemark)
                    .disabled(!enableButtonBool)
                    .padding()
                    .background(Color(.white))
                    .cornerRadius(8)
                    .accentColor(.gray)
            } else {
                TextField("Remarks", text: currentRemark)
                    .disabled(!enableButtonBool)
                    .padding()
                    .background(Color(.white))
                    .cornerRadius(8)
            }
        }
        .padding()
        .background(Color("light_gray"))
        .foregroundColor(.black)
        .cornerRadius(8)
        .shadow(radius: 10)
        .padding()
    }
    
    func addData() {
        statusList.append("Yes")
        statusList.append("No")
        statusList.append("NA")
        if let status = checkListData.status {
            statusList.append(status) //<- Why this is needed?
        }
    }
}

struct CheckListSheet: View {
    @State var taskId: Int
    
    @State var checklistResponse: [ChecklistModel] = [ChecklistModel()]
    @State var enableButtonBool: Bool = false
    
    @State var statusList: [String] = []
    
    var body: some View {
        Text("Checklist Items")
            .padding()
            .font(.title)
        ScrollView {
            ForEach(checklistResponse.indices, id:\.self) { index in
                CheckListCard(checkListData: $checklistResponse[index],
                              enableButtonBool: $enableButtonBool)
            }
            if enableButtonBool {
                Button("Update") {
                    updateCheckList()
                }
            }
        }
        .onAppear(){
            getChecklists()
        }
    }
    
    func updateCheckList()  {
        print("this is the checklistresponse \(checklistResponse)")
    }
    
    func getChecklists()  {
        checklistResponse = [
            ChecklistModel(description: "ist item", remarks: nil, status: "Yes"),
            ChecklistModel(description: "2nd item", remarks: "Fine", status: "no")
        ]
        enableButtonBool = true
    }
}
Get data from view after it gets added on screen using ForEach
 
 
Q