1

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!

6
  • 2
    You could just use another variable, eg Picker("Berichtsart", selection: $myvar) and corresponding .onChange(of: myvar) where you update the report.reportType as well as do your code just for Picker selection. Commented Jun 9, 2024 at 11:59
  • @workingdogsupportUkraine Thanks, but I tried that and it didn't solve my dilemma. The problem is that I then need to listen to changes of report as well, so I can update myvvar in case reportchanges. This update will then trigger the code that I want to run only on Picker selection. Commented Jun 9, 2024 at 13:12
  • It should still work, you can check if myvar and reportType is the same or not in the onChange for reportType. Commented Jun 9, 2024 at 13:35
  • @JoakimDanielson I think it does not matter. If myvar changes, I can update report without a problem. But how will be myvar be updated, in case report.reportType changes through some other user interaction? The way I do it (which might be wrong) is to listen to changes to report, and then update myvar, which then triggers the code I only want to run when the user uses the picker. Commented Jun 9, 2024 at 14:08
  • myvar should only be updated via the Picker. Commented Jun 9, 2024 at 14:12

1 Answer 1

2

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.
Sign up to request clarification or add additional context in comments.

2 Comments

Wow, nice one. I will give this a try and report back. Looks very promising! Thanks.
Worked like a charm. The only caveat at the moment seems to be that Xcode does not catch any breakpoints that are within the set: closure. But this has nothing to do with the quality and correctness of the answer. Thanks again!!!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.