2

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!

ej5607
  • 309
  • 2
  • 12
  • No crash here. Works fine. Xcode 13.2b, iOS 15.2. – Yrb Nov 23 '21 at 13:59
  • Yrb - That worked! I downloaded the latest beta version of Xcode and the problem disappeared. Thank you for that suggestion! – ej5607 Nov 24 '21 at 14:05
  • That being said, Underthestars-zhy is correct that taking the view in and out of the view hierarchy can be problematic. I would follow that answer to make sure it doesn't break in the future. – Yrb Nov 24 '21 at 14:08
  • Ah, good to know and will do re: Underthestars-zhy's recommendation. Many thanks to both of you. – ej5607 Nov 24 '21 at 17:21

1 Answers1

2

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)
  • Thank you for this suggestion. Unfortunately, it doesn't appear to solve the problem. After adding this code to Picker Two, the app still crashes if I change the Picker One selection to display Picker Three, then change the Picker One selection back to Picker Two and attempt to change the Picker Two selection. Is there something else that I need to do elsewhere in my code to make this solution work? – ej5607 Nov 24 '21 at 13:15