How to Call a Function When a Swiftui Picker Selection Changes

Is there a way to call a function when a SwiftUI Picker selection changes?

If the @State value will be used in a View, you don't need extra variable name

  struct BuilderPicker: View {
// let name: String = ""
let options: Array<String> = ["1", "2","3","4","5"]
@State var selectedOption = 0
var body: some View {
HStack {
Text(options[selectedOption])
.font(.body)
.padding(.leading, 10)
Picker(selection: $selectedOption, label: Text(options[selectedOption])) {
ForEach(0 ..< options.count) {
Text(self.options[$0]).tag($0)
}
}.pickerStyle(SegmentedPickerStyle())
.padding(.trailing, 25)}
// }.onTapGesture {
// self.selectedOption = self.selectedOption == 0 ? 1 : 0
// }
.padding(.init(top: 10, leading: 10, bottom: 10, trailing: 0))
.border(Color.secondary, width: 3)
.padding(.init(top: 0, leading: 15, bottom: 0, trailing: 15))
.font(.body)
}


}

If you need separated operation on the @State, the simplest way is adding one line : onReceive() to the view.

  HStack {
Text("")
.font(.body)
.padding(.leading, 10)
Picker(selection: $selectedOption, label: Text("")) {
ForEach(0 ..< options.count) {
Text(self.options[$0]).tag($0)
}
}.pickerStyle(SegmentedPickerStyle())
.padding(.trailing, 25)}
// }.onTapGesture {
// self.selectedOption = self.selectedOption == 0 ? 1 : 0
// }
.padding(.init(top: 10, leading: 10, bottom: 10, trailing: 0))
.border(Color.secondary, width: 3)
.padding(.init(top: 0, leading: 15, bottom: 0, trailing: 15))
.font(.body)
.onReceive([self.selectedOption].publisher.first()) { (value) in
print(value)
}

Is there a way to call a function when a SwiftUI Picker selection changes an EnvironmentObject?

There is much simpler approach, which looks more appropriate for me.

The generic schema as follows

Picker("Label", selection: Binding(    // proxy binding
get: { self.viewModel.value }, // get value from storage
set: {
self.viewModel.value = $0 // set value to storage

self.anySideEffectFunction() // didSet function
}) {
// picker content
}

How to call on a view when a Picker selection is selected

Here is an example for solving your problem:

enum CameraEnum: String, CaseIterable { case telephoto, wide, ultraWide}

struct ContentView: View {

@State private var selection: CameraEnum = .wide

var body: some View {

Picker(selection: $selection, label: EmptyView(), content: {

ForEach(CameraEnum.allCases, id:\.self) { item in

Text(item.rawValue)

}

})
.onAppear() { cameraFunction(selection) }
.onChange(of: selection, perform: { value in cameraFunction(value) })

}

private func cameraFunction(_ cameraValue: CameraEnum) {
print("Your camera selection is:", cameraValue)
}

}

SwiftUI Picker OnChange not being called when new option is being selected

For seasonSelection to work in a Picker, add a .tag() to Text(...) and make sure it is of the same type as the seasonSelection, for example (assuming index is of type Int):

NOTE: this works for macCatalyst, for macOS only, see EDIT.

Picker(selection: $seasonSelection, label: Text("Selected Season")){
ForEach(userData.userData.lastUsedSeason, id:\.self) { index in
Text(seasonData.seasons[index].seasonName()).tag(index) // <-- here
}
}
.onChange(of: seasonSelection, perform: { _ in
seasonData.seasons[0].testPrint()
})

EDIT: sample code that shows how to use tag with seasonSelection in .onReceive() on macOS only, and .onChange() for macCatalyst.

import SwiftUI
import Combine

@main
struct TestMacApp: App {
var body: some Scene {
WindowGroup {
Text("demo").frame(width: 480.0, height: 320.0)
}
.commands {
SandStatCommands()
}
}
}

struct SandStatCommands: Commands {
@State var seasons = ["Spring","summer","autumn","winter"] // array of values
@State var seasonSelection: Int = 0 // <-- here initial selection

var body: some Commands {
CommandMenu("Season"){
Button("Add Season"){
seasons.append(String(UUID().uuidString.prefix(6))) // for demo, add a random name
}
Picker(selection: $seasonSelection, label: Text("Selected Season")){
ForEach(0..<seasons.count, id:\.self) { index in
Text(seasons[index]).tag(index) // <-- here tag
}
}
// .onChange(of: seasonSelection) { value in
// print("---> seasonSelection value: \(value)")
// }
.onReceive(Just(seasonSelection)) { value in
print("---> seasonSelection value: \(value)")
}
}
}
}

SwiftUI Picker onChange or equivalent?

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)
})
}
}

How do I add action on picker on selection of item in swiftUI?

I found 3 different ways to achieve this. The last one I took here. So, there are all these ways, you can choose which you want:

struct PickerOnChange: View {

private var options = ["add", "edit", "delete"]
@State private var selectedOption = 0
@State private var choosed = 0

var body: some View {

VStack {

Picker(selection: $selectedOption.onChange(changeViewWithThirdWay), label: Text("Choose action")) {
ForEach(0 ..< self.options.count) {
Text(self.options[$0]).tag($0)
}
}.pickerStyle(SegmentedPickerStyle())

// MARK: first way
VStack {
if selectedOption == 0 {
Text("add (first way)")
} else if selectedOption == 1 {
Text("edit (first way)")
} else {
Text("delete (first way)")
}

Divider()

// MARK: second way
ZStack {

AddView()
.opacity(selectedOption == 0 ? 1 : 0)

EditView()
.opacity(selectedOption == 1 ? 1 : 0)

DeleteView()
.opacity(selectedOption == 2 ? 1 : 0)
}

Divider()

// MARK: showing third way
Text("just to show, how to use third way: \(self.choosed)")

Spacer()

}

}

}

func changeViewWithThirdWay(_ newValue: Int) {
print("will change something in third way with: \(choosed), you can do everything in this function")
withAnimation {
choosed = newValue
}

}

}

// MARK: the third way
extension Binding {
func onChange(_ handler: @escaping (Value) -> Void) -> Binding<Value> {
return Binding(
get: { self.wrappedValue },
set: { selection in
self.wrappedValue = selection
handler(selection)
})
}
}

You'll achieve this with code snippet:

Sample Image

Is there a way to call a function when a slider is used in swift?

We can use computable in-inline binding with side-effect for this (and you can remove onChange at all), here is a demo

 Slider(value: Binding(get: { noteLength }, 
set: {
// willSet side-effect here // << here !!
noteLenght = $0
// didSet side-effect here // << here !!
}),
in: 0.1...3.01, step: 0.1)
.onChange(of: noteLength, perform: { value in
defaults.set(value, forKey: "noteLengthEx1")
levelIndex = 4
})

SwiftUI Picker not changing selection value

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
}
}

}


Related Topics



Leave a reply



Submit