Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI Picker onChange or equivalent?

Tags:

swiftui

I want to change another unrelated @State variable when a Picker gets changed, but there is no onChanged and it's not possible to put a didSet on the pickers @State. Is there another way to solve this?

like image 592
trapper Avatar asked Aug 16 '19 04:08

trapper


2 Answers

Deployment target of iOS 14 or newer

Apple has provided a built in onChange extension to View, which can be used like this:

struct MyPicker: View {
    @State private var favoriteColor = 0

    var body: some View {
        Picker(selection: $favoriteColor, label: Text("Color")) {
            Text("Red").tag(0)
            Text("Green").tag(1)
        }
        .onChange(of: favoriteColor) { tag in print("Color tag: \(tag)") }
    }
}

Deployment target of iOS 13 or older

struct MyPicker: View {
    @State private var favoriteColor = 0

    var body: some View {
        Picker(selection: $favoriteColor.onChange(colorChange), label: Text("Color")) {
            Text("Red").tag(0)
            Text("Green").tag(1)
        }
    }

    func colorChange(_ tag: Int) {
        print("Color tag: \(tag)")
    }
}

Using this helper

extension Binding {
    func onChange(_ handler: @escaping (Value) -> Void) -> Binding<Value> {
        return Binding(
            get: { self.wrappedValue },
            set: { selection in
                self.wrappedValue = selection
                handler(selection)
        })
    }
}
like image 77
ccwasden Avatar answered Nov 14 '22 13:11

ccwasden


First of all, full credit to ccwasden for the best answer. I had to modify it slightly to make it work for me, so I'm answering this question hoping someone else will find it useful as well.

Here's what I ended up with (tested on iOS 14 GM with Xcode 12 GM)

struct SwiftUIView: View {
    @State private var selection = 0

    var body: some View {
        Picker(selection: $selection, label: Text("Some Label")) {
            ForEach(0 ..< 5) {
                Text("Number \($0)") }
        }.onChange(of: selection) { _ in
            print(selection)
        }
        
    }
}

The inclusion of the "_ in" was what I needed. Without it, I got the error "Cannot convert value of type 'Int' to expected argument type '()'"

like image 22
snowskeleton Avatar answered Nov 14 '22 13:11

snowskeleton