skip to Main Content

I am new to swift so I apologize for my ignorance but here is my issue please correct me if I have anything wrong. I am learning.

I have three pickers that in a hierarchy. Picker 1 (War Period), Picker 2’s options (Nation) depend on Picker 1’s selection, and Picker 3’s options (Force Diagram) depend on Picker 2’s selection.

When I select the pickers in order the selections are displayed properly. However, if I were to go back and change picker 1’s selection, picker 2s options get changed but not picker 3. If I change picker 2’s selection, picker 3 updates properly. How do I update all the selections to show properly when any picker is changed? See the below code.

import SwiftUI

struct Testing_View: View {

    @State private var warPeriods = ""
    @State private var selectedNation = ""
    @State private var forceDiagram = ""
    
    // Select war periods
    let periods = ["Early War", "Mid-War", "Late War"]
    
    // Select Nation options
    let nations: [String: [String]] = [
        "Early War": ["Early American", "Early Axis-Allies", "Early British", "Early German", "Early Soviet"],
        "Mid-War": ["Eastern Front", "North Africa"],
        "Late War": ["American", "Axis-Allies", "British", "German", "Soviet"]
    ]
    
    // Select Force Diagram options
    let diagrams: [String: [String]] = [
        "Early American" : ["E-Day: American", "EBulge: American"],
        "Early Axis-Allies" : ["EBagration: Axis-Allies"],
        "Early British": ["ED-Day: British", "EBulge: British"],
        "Early German": ["ED-Day: German", "ED-Day: Waffen-SS", "EBagration: German", "EBulge: German", "EBerlin: German"],
        "Early Soviet": ["EBagration: Soviet", "EBerlin: Soviet"],
        "Eastern Front" : ["German: Ghost Panzers", "German: Iron Cross", "Soviet: Enemy at the Gate", "Soviet: Red Banner"],
        "North Africa" : ["British: Armoured Fist", "German: Afrika Korps", "Italian: Avanti", "American: Fighting First"],
        "American" : ["D-Day: American", "Bulge: American"],
        "Axis-Allies" : ["Bagration: Axis-Allies"],
        "British": ["D-Day: British", "Bulge: British"],
        "German": ["D-Day: German", "D-Day: Waffen-SS", "Bagration: German", "Bulge: German", "Berlin: German"],
        "Soviet": ["Bagration: Soviet", "Berlin: Soviet"]
    ]
    
    var body: some View {
        NavigationView {
            Form {
                Picker("Select War Period:", selection: $warPeriods) {
                    ForEach(periods, id: .self) {
                        Text($0)
                    }
                }
                //.pickerStyle(.segmented)
                
                Picker("Select Nation:", selection: $selectedNation) {
                    ForEach(nations[warPeriods] ?? [], id: .self) { subOption in
                        Text(subOption)
                    }
                }
                
                Picker("Select Force Diagram:", selection: $forceDiagram) {
                    ForEach(diagrams[selectedNation] ?? [], id: .self) { subSubOption in
                        Text(subSubOption)
                    }
                }
            }
        }
    }
}

#Preview {
    Testing_View()
}

I have tried making separate arrays for each option and using if else statements for the pickers to manually do this but the results were the same.

2

Answers


  1. First I would add two array properties instead of using the dictionaries

    @State private var nationsForPeriod = [String]()
    @State private var diagramsForNation = [String]()
    

    Then you also need to fix the Picker components so that each item gets a tag for matching against the selection property and you also need a choice for nothing selected ("")

    Picker("Select War Period:", selection: $warPeriods) {
        Text("Not selected").tag("")
        ForEach(periods, id: .self) {
            Text($0).tag($0)
        }
    }
    

    The same change is needed for all pickers.

    Then I would use the onChange modifier to keep the next selection and array properties up to date when a previous selection changes.

    Picker("Select War Period:", selection: $warPeriods) {
        //...
    }
    .onChange(of: warPeriods) {
        selectedNation = ""
        nationsForPeriod = nations[warPeriods] ?? []
    }
    
    Picker("Select Nation:", selection: $selectedNation) {
        //...
    }
     .onChange(of: selectedNation) {
            forceDiagram = ""
            diagramsForNation = diagrams[selectedNation] ?? []
        }
    }
    

    The whole Form part

    Form {
        Picker("Select War Period:", selection: $warPeriods) {
            Text("Not selected").tag("")
            ForEach(periods, id: .self) {
                Text($0).tag($0)
            }
        }
        .onChange(of: warPeriods) {
            selectedNation = ""
            nationsForPeriod = nations[warPeriods] ?? []
        }
        
        Picker("Select Nation:", selection: $selectedNation) {
            Text("Not selected").tag("")
            ForEach(nationsForPeriod, id: .self) { subOption in
                
                Text(subOption).tag(subOption)
            }
            .onChange(of: selectedNation) {
                forceDiagram = ""
                diagramsForNation = diagrams[selectedNation] ?? []
            }
        }
        
        Picker("Select Force Diagram:", selection: $forceDiagram) {
            Text("Not selected").tag("")
            ForEach(diagramsForNation, id: .self) { subSubOption in
                Text(subSubOption).tag(subSubOption)
            }
        }
    }
    
    Login or Signup to reply.
  2. Please excuse my English.

    The reason is when you change first picker selection, the selected values of second and third pickers aren’t changed. As the picker2 is shown based the selection of picker1, so picker2’s options are changed. But as picker2’s selected value isn’t changed, so picker3’s options aren’t changed.

    Set default selection value when parent picker’s selection updated.

    Form {
        Picker("Select War Period:", selection: $warPeriods) {
            ForEach(periods, id: .self) {
                Text($0)
            }
        }
        .onChange(of: warPeriods) {
            selectedNation = ""
        }
    
        Picker("Select Nation:", selection: $selectedNation) {
            ForEach(nations[warPeriods] ?? [], id: .self) { subOption in
                Text(subOption)
            }
        }
        .onChange(of: selectedNation) {
            forceDiagram = ""
        }
    
        Picker("Select Force Diagram:", selection: $forceDiagram) {
            ForEach(diagrams[selectedNation] ?? [], id: .self) { subSubOption in
                Text(subSubOption)
            }
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search