Search code examples
iosswift

How to generate a VStack that contains a collection of textfields on clicking a button in Swift iOS?


I am a beginner in Swift and learning to create simple pages in apps. As part of that I am trying to save education details from users in the registration process. Since a person can have multiple degrees and types, I added UG, PG & Ph.D to the types. Below is the view:

struct AutoView: View {
    @State private var showGraduationDetails = false
    let graduationTypes = ["Under Graduation", "Post Graduation", "Ph.D"]
    @State private var graduationType = "" // New state variable to store graduation type
    @State private var specialisation = "" // New state variable to store graduation type
    @State private var graduationUniversity = "" // New state variable to store graduation type
    @State private var cityOfUniversity = "" // New state variable to store graduation type

    var body: some View {
        ZStack { // Use ZStack to layer content on top of the background
            LinearGradient(gradient: Gradient(colors: [.mint, .gray]), startPoint: .topLeading, endPoint: .bottomTrailing)
            .ignoresSafeArea() // Extend gradient to entire screen
            ScrollView {
                VStack(spacing: 20) { // Add spacing between elements
                    VStack {
                        // Graduation details (conditionally displayed)
                        if showGraduationDetails {
                            VStack(alignment: .leading) {
                                ZStack { // Create a ZStack for the section
                                    RoundedRectangle(cornerRadius: 20) // Rounded rectangle with translucent border
                                        .fill(Color.white.opacity(0.1)) // Adjust opacity for translucency
                                        .frame(width: 365) // Allow horizontal expansion
                                        .frame(height: 50 /* Adjust height based on content */)
                                    Picker("Graduation Type", selection: $graduationType) { // Use Picker for dropdown
                                        Text("Select Graduation Type").tag("Select Graduation Type")
                                        ForEach(graduationTypes, id: \.self) { type in
                                            Text(type)
                                        }
                                    }
                                    .pickerStyle(.menu)
                                }

                                // Specialisation
                                TextField("Degree Specialisation", text: $specialisation)
                                    .foregroundColor(.white)
                                    .padding()
                                    .background(Color.white.opacity(0.2))
                                    .cornerRadius(20)
                                // University of graduation
                                TextField("University of graduation", text: $graduationUniversity)
                                    .foregroundColor(.white)
                                    .padding()
                                    .background(Color.white.opacity(0.2))
                                    .cornerRadius(20)
                                // City of University
                                TextField("City of University", text: $cityOfUniversity)
                                    .foregroundColor(.white)
                                    .padding()
                                    .background(Color.white.opacity(0.2))
                                    .cornerRadius(20)
                            }
                        }
                    }
                    Button(action: {
                        showGraduationDetails.toggle() // Toggle visibility on click
                    }) {
                        Text(showGraduationDetails ? "Add more degree(s)" : "Add your degree(s)")
                            .fontWeight(.bold)
                            .foregroundColor(.yellow)
                    }
                    .frame(width: 170, height: 20, alignment: .center)
                    .padding()
                    .background(Color(UIColor.systemBlue).opacity(0.9))
                    .cornerRadius(20)
                }
                .padding() // Add padding around content
            }
        }
    }
}

How it looks: enter image description here

Upon clicking the button, I could see the Textfields generated as below. enter image description here

Could anyone let me know if there is a way I can generate same set of fields to add another education type by clicking on the same button, basically adding n-number of education details. Any help is much appreciated.


Solution

  • If you want to be able to capture information about multiple degrees then you need to use a collection of instances.

    The information about a single degree can be factored out into an Identifiable struct:

    struct DegreeInfo: Identifiable {
        let id = UUID()
        var graduationType = ""
        var specialisation = ""
        var graduationUniversity = ""
        var cityOfUniversity = ""
    }
    

    Then the part of the view that allows this information to be edited can be factored out as a separate view:

    struct DegreeInfoView: View {
        let graduationTypes = ["Under Graduation", "Post Graduation", "Ph.D"]
        @Binding var degreeInfo: DegreeInfo
    
        var body: some View {
            VStack(alignment: .leading) {
                ZStack { // Create a ZStack for the section
                    RoundedRectangle(cornerRadius: 20) // Rounded rectangle with translucent border
                        .fill(Color.white.opacity(0.1)) // Adjust opacity for translucency
                        .frame(width: 365) // Allow horizontal expansion
                        .frame(height: 50 /* Adjust height based on content */)
                    Picker("Graduation Type", selection: $degreeInfo.graduationType) { // Use Picker for dropdown
                        Text("Select Graduation Type").tag("Select Graduation Type")
                        ForEach(graduationTypes, id: \.self) { type in
                            Text(type)
                        }
                    }
                    .pickerStyle(.menu)
                }
    
                // Specialisation
                TextField("Degree Specialisation", text: $degreeInfo.specialisation)
                    .foregroundStyle(.white)
                    .padding()
                    .background(Color.white.opacity(0.2))
                    .cornerRadius(20)
                // University of graduation
                TextField("University of graduation", text: $degreeInfo.graduationUniversity)
                    .foregroundStyle(.white)
                    .padding()
                    .background(Color.white.opacity(0.2))
                    .cornerRadius(20)
                // City of University
                TextField("City of University", text: $degreeInfo.cityOfUniversity)
                    .foregroundStyle(.white)
                    .padding()
                    .background(Color.white.opacity(0.2))
                    .cornerRadius(20)
            }
        }
    }
    

    Now you just need to manage the collection of degree info. An array can be used for this. The button appends a new instance to the array:

    struct AutoView: View {
        @State private var academicInfo = [DegreeInfo]()
    
        var body: some View {
            ZStack { // Use ZStack to layer content on top of the background
                LinearGradient(gradient: Gradient(colors: [.mint, .gray]), startPoint: .topLeading, endPoint: .bottomTrailing)
                    .ignoresSafeArea() // Extend gradient to entire screen
                ScrollView {
                    VStack(spacing: 20) { // Add spacing between elements
                        ForEach($academicInfo) { degreeInfo in
                            DegreeInfoView(degreeInfo: degreeInfo)
                        }
                        Button {
                            academicInfo.append(DegreeInfo())
                        } label: {
                            Text(academicInfo.isEmpty ? "Add your degree(s)" : "Add more degree(s)")
                                .fontWeight(.bold)
                                .foregroundStyle(.yellow)
                                .padding()
                                .background(.blue.opacity(0.9))
                                .clipShape(RoundedRectangle(cornerRadius: 20))
                        }
                    }
                    .padding() // Add padding around content
                }
            }
        }
    }