Search code examples
swiftuionchangepicker

SwiftUI Picker: Run code only when user changes the state of the picker


In my SwiftUI app on macOS Sonoma I am using several Picker views. Here is one example:

Picker("Berichtsart", selection: $report.reportType) {
    ForEach(ReportType.allCases) { option in
        Image(systemName: option.iconString)
            .help(option.description)
            
    }
}
.fixedSize()
.pickerStyle(.segmented)
.labelsHidden()
.onChange(of: report.reportType) {
    // Do something here when the user changes the selection thru the picker
    reports.push(report)
}

The onChange closure get executed every time report.reportType changes. It runs when the user clicks on a different segment of the Picker, but it also runs if some other code changes the value of the Binding.

But I am looking for a way to run the code in the onChange only when the user triggers the change through the picker, and not, when some other code changes report.

TLDR: I want to run some code when a user actively changes the selection on a Picker, but not if something else changes the Binding the picker uses to store its selection.

Any hints and pointers are greatly appreciated. Thanks!


Solution

  • You can make a simple interceptor and use it where you intended (like the picker selection) instead of directly passing the source like:

    var pickerSelectionInterceptor = Binding<ReportType>(
        get: { report.reportType },
        set: {
            report.reportType = $0
            print("Changed \(report.reportType)") // 👈 Do your stuff here
        }
    )
    
    Picker("Berichtsart", selection: pickerSelectionInterceptor) { ... }
    /* .onChange(of: report.reportType) { ... } */ // 👈 No need to observe the source changes for this specific reason.