Search code examples
xcodeswiftuipicker

SwiftUI Dynamic Pickers in Form crash


I'm a novice SwiftUI developer, and I'm having trouble creating a view that dynamically presents different pickers based upon a user's previous selection in that view. To see the problem, run the following code in the Simulator, change the pickerOneSelection to "Picker Three", then change it BACK to "Picker Two" and attempt to change the pickerTwoSelection. The app will immediately crash; the specific error with this toy example is "Thread 1: EXC_BAD_ACCESS (code=1, address=0x8)". Any insight is most welcome!

As an aside, I have looked at these questions: (SwiftUI hierarchical Picker with dynamic data crashes) and (swiftUi : 2 Pickers on one screen - app crash with "Index out of range"), but if possible, I'd like to avoid converting my string selection variables (pickerOneSelection, pickerThreeSelection) into integers, which must then be translated back into strings to get the text associated with the user's selection. If it's not possible to do what I'm trying to do without the index variables, if you could help me understand why, that would be very helpful.

import SwiftUI

struct ContentView: View {
    @State var pickerOneSelection: String = "Picker Two"
    @State var pickerTwoVisible: Bool = false
    @State var pickerTwoSelection: Int = 3
    @State var pickerThreeSelection: String = "Option 1"
    
    var pickerOneOptions = ["Picker Two", "Picker Three"]
    var pickerThreeOptions = ["Option 1", "Option 2", "Option 3"]
    
    var body: some View {
        NavigationView {
            Form {
                Picker("Picker One selection", selection: $pickerOneSelection) {
                    ForEach(pickerOneOptions, id: \.self) {
                        Text($0)
                    }
                } // End of Picker 1
                
                if pickerOneSelection == "Picker Two" {
                    HStack {
                        Text("Picker Two selection")
                        Spacer()
                        Button(String(pickerTwoSelection)) {
                            self.pickerTwoVisible.toggle()
                        }
                    } // End of HStack
                    
                    if pickerTwoVisible {
                        Picker("Picker Two", selection: $pickerTwoSelection) {
                            ForEach(0..<32, id: \.self) {
                                Text(String($0))
                            }
                        }
                        .pickerStyle(WheelPickerStyle())
                    } // End of pickerTwoVisible
                    
                } else {
                    
                    Picker("Picker Three selection", selection: $pickerThreeSelection) {
                        ForEach(pickerThreeOptions, id: \.self) {
                            Text($0)
                        }
                    } // End of Picker 3
                    
                }
            } // End of Form
        } // End of NavigationView
    } // End of body
} // End of ContentView

Many thanks for your help and expertise!


Solution

  • The key to the problem is that you hide the picker, so it is best to set its transparency to 0

    Picker("Picker Two", selection: $pickerTwoSelection) {
                                ForEach(1..<32, id: \.self) {
                                    Text(String($0))
                                }
                            }
                            .pickerStyle(WheelPickerStyle())
                            .opacity(pickerTwoVisible ? 1 : 0)
                            .frame(width: pickerTwoVisible ? nil : 0, height: pickerTwoVisible ? nil : 0)