2

The following picker isn't updating $selection. Regardless of what the Picker shows while running the app, selection.rawValue always returns 0.

What is preventing the Picker from updating the State variable selection?

import SwiftUI

struct OrderPicker: View {
    
    let initialIndex: Int
    @State private var selection: Index
    
    enum Index: Int, CaseIterable, Identifiable {
        case none = 0,
             first = 1,
             second = 2,
             third = 3
        var id: Self { self }
    }
    
    init(initialIndex: Int) {
        self.initialIndex = initialIndex
        _selection = State(initialValue: OrderPicker.Index(rawValue: initialIndex) ?? .none)
    }
    
    var body: some View {
        Form {
            Picker("Order in list", selection: $selection) {
                ForEach(Index.allCases) { index in
                    Text(String(describing: index)).tag(index)
                }
            }
        }
        .frame(height: 116)
    }
    
    func getOrderIndex() -> Int {
        let index = selection.rawValue
        return index
    }
}
5
  • 1
    Your code is behaving as expected and is updating selection. You can verify this by adding .onChange(of: selection) { print($0) } to the end of your Form. However, the presence of getOrderIndex, which is unused in your current code, makes me wonder if you're trying to do something like reach into a child view to see it's state from the parent view or something like that.
    – jnpdx
    Commented Mar 18, 2022 at 3:36
  • 1
    You need to get rid of those initialization, no need to them, also need to use @Binding, and also using a style for picker as well.
    – swiftPunk
    Commented Mar 18, 2022 at 3:41
  • @jnpdx You're correct, I am trying to read from selection in a parent class by exposing it with getOrderIndex. Will getOrderIndex not accurately return selection's rawValue? Commented Mar 18, 2022 at 4:00
  • 1
    You cannot reach into child views that way. Shared state should be stored in a parent view and passed down via Binding if necessary.
    – jnpdx
    Commented Mar 18, 2022 at 4:04
  • Got it. Swapping my State for a Binding worked! Commented Mar 18, 2022 at 4:19

1 Answer 1

1

Here is an approach for you:

struct ContentView: View {
    
    @State private var selection: PickerType = PickerType.none
    
    var body: some View {
        
        OrderPicker(selection: $selection)
    }
}

struct OrderPicker: View {
    
    @Binding var selection: PickerType
    
    var body: some View {
        Form {
            Picker("Order in list", selection: $selection) {
                ForEach(PickerType.allCases, id: \.self) { item in
                    Text(item.rawValue)
                    
                }
            }
            .pickerStyle(WheelPickerStyle())
            .onChange(of: selection, perform: { newValue in
                print(newValue.rawValue, newValue.intValue)
            })
        }
        
    }

}

enum PickerType: String, CaseIterable {
    
    case none, first, second, third
    
    var intValue: Int {
        switch self {
        case .none: return 0
        case .first: return 1
        case .second: return 2
        case .third: return 3
        }
    }
    
}

Not the answer you're looking for? Browse other questions tagged or ask your own question.