Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding a callback to a SwiftUI Picker

I'm trying to add a callback to a SwiftUI picker but can't get it to execute. The didSet apparently does not execute when picker value changes. Here's what I've tried so far:

struct ContentView : View {
    @State private var picked: Int = 0 {didSet{print("here")}}
    var someData = ["a", "b", "c"]
    var body: some View {

        VStack {
            Picker(selection: $picked,
                   label: Text("")) {
                    ForEach(0 ..< someData.count)     {Text(self.someData[$0]).tag($0)}
            }
            .pickerStyle(.wheel)
            Text("you picked: \(someData[picked])")
        }
    }
}
like image 646
slicerdicer Avatar asked Jul 17 '19 22:07

slicerdicer


3 Answers

Code updated to beta 6

The @State variable will never execute didSet, because it does not change. What does change is the wrapped value. So the only way I can think to tap it, is by putting a second binding in the middle, to serve as an intermediary, and pass the value along. This way, you can put your callback there.

struct ContentView : View {
    @State private var picked: Int = 0

    var someData = ["a", "b", "c"]
    var body: some View {

        let p = Binding<Int>(get: {
            return self.picked
        }, set: {
            self.picked = $0

            // your callback goes here
            print("setting value \($0)")
        })


        return VStack {
            Picker(selection: p,
                   label: Text("")) {
                    ForEach(0 ..< someData.count) {
                        Text(self.someData[$0]).tag($0)
                    }
            }
            .pickerStyle(WheelPickerStyle())
            Text("you picked: \(someData[picked])")
        }
    }
}
like image 154
kontiki Avatar answered Nov 12 '22 09:11

kontiki


you can use ObservableObject to workaround it


import SwiftUI

class PickerModel:ObservableObject{
    @Published var picked: Int = 0 {didSet{print("here")}}
}

struct Picker_Callback: View {
   @ObservedObject var pickerModal = PickerModel()
    var someData = ["a", "b", "c"]
    var body: some View {

        VStack {
            Picker(selection: self.$pickerModal.picked,
                   label: Text("")) {
                    ForEach(0 ..< someData.count)     {Text(self.someData[$0]).tag($0)}
            }
            //.pickerStyle(.wheel)
            Text("you picked: \(someData[self.pickerModal.picked])")
        }
    }
}

like image 34
springday Avatar answered Nov 12 '22 08:11

springday


You can't use the didSet modifier on a variable with the @State property wrapper.

The State wrapper tells SwiftUI that the wrapped value must be managed by the SwiftUI framework itself. This means that the view doesn't actually contains the value, it only contains a State (which is immutable) that provide some kind of reference to the wrapped value.

Actually, you should forget about the didSet callback inside a SwiftUI View. The View protocol requires a body, which is not marked as mutating (SwiftUI views are struct). So a View is basically immutable.

like image 2
rraphael Avatar answered Nov 12 '22 08:11

rraphael