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:
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
How to Get Directory Size with Swift on Os X
Class 'Viewcontroller' Has No Initializers in Swift
Swift Date(Byadding:To:) Returns Nil for Trivial Calculation in Repl
Initialize Class-Instance and Access Variables in Swift
Swiftui iOS 14 Widget Countdown
How to Get Rgb Components from Color in Swiftui
Is There a Zip Function to Create Tuples with More Than 2 Elements
Using Foreach Loop with Binding Causes Index Out of Range When Array Shrinks (Swiftui)
Expected to Decode Array<Any> But Found a Dictionary Instead
How to Get Indexpath When Image Inside Cell Tapped
Swift Repl: How to Import, Load, Evaluate, or Require a .Swift File
Different Ways to Initialize a Dictionary in Swift
Is Swift Dictionary of Indexed for Performance? Even for Exotic Types (Uuid)
Highlight a Specific Part of the Text in Swiftui
Troubles With Starting Value Using Uislider
How to Properly Use Queryorderedbyvalue
Multiple Type Constraints in Swift
Every Uialertcontroller Disappear Automatically Before User Responds - Since iOS 13