schulzy
schulzy

Reputation: 23

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

SeasonData is an array of objects of type Season

userData holds 2 [INT] lastUsedSeason -> which reference the last 3array positions of SeasonData that were accessed [3,1,0]

arrayPosition holds [0,1,2].

These are loaded from a file.

The display and selection of this picker works correctly. However I cannot figure out how to get the onChange to fire when changing a selection.

    struct UserData: Codable {
      var lastUsedSeason: [Int]
      var arrayPosition: [Int]
    
     mutating func addLastUsedSeason(index: Int) {
        if (lastUsedSeason.count > 3){
          lastUsedSeason.insert(index, at: 0)
          lastUsedSeason.remove(at: 2)
        }
      }
    }

    @main
    struct SandStats_New_App: App {
      @StateObject private var modelData = ModelData()
      
      @State private var showSeason = false
      
      
        var body: some Scene {
            WindowGroup {
              ContentView(seasonSelected: $showSeason)
                .environmentObject(modelData)
                .frame(minWidth: 700, minHeight: 300, alignment: .topLeading)
                .background(Color.red)
            }
            .commands {
              SandStatCommands(addSeasonButton: $showSeason, modelData: modelData)
            }
        }
    }

struct SandStatCommands: Commands {
  var modelData : ModelData
  @Binding var addSeasonButton: Bool
  @State var seasonSelection: Int = 0

  var body: some Commands {
    
      CommandMenu("Season"){
        
        Button("Add Season"){
          addSeasonButton = true
        }
        Picker(selection: $seasonSelection, label: Text("Selected Season")){
          ForEach(modelData.userData.arrayPosition, id:\.self) { index in
            Text(modelData.seasons[modelData.userData.getLastUsedSeason(index1: index)].seasonName()).tag(index)
          }
        }.onChange(of: seasonSelection, perform:  { value in
          print(value)
        })
        
        Button("save Index"){
          saveData(for: modelData.userData, to: "userData")
        }
      }
      
    }
}

Upvotes: 2

Views: 2969

Answers (2)

jeet.chanchawat
jeet.chanchawat

Reputation: 2575

For future users long story short:

onChange does not gets called when 'seasonSelection' is Int and you are trying to set String to it.

XCode does not gives any error message/crash to figure out what's wrong.

  @State var seasonSelection: String = "default" // keep this 'default' as the value you want to show on opening picker
  //leaving it blank will select 0th index ""

Upvotes: 0

dudette
dudette

Reputation: 856

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

Upvotes: 1

Related Questions