Search code examples
swiftuiobservablepicker

Picker drops selected segment when data changed


I got a problem with Picker. When I try to change state of data in selected segment it's drops it (gif attached). How I can have stay selected segment in Picker when displaying data is changed (when I press "love" button, selected segment should stay without changes)

Single item has option:

struct Landmark: Identifiable, Codable, Hashable {
var id: Int
var name: String
var imageName: String
var mainImage: Image {
    Image(imageName)
var liked: Bool
var popular: Bool
var recommended: Bool
}

I got class with array of objects:

final class ModelData: ObservableObject {
@Published var landmarks: [Landmark]
}

And trying use picker like that:

struct tempSegmentControl: View {

@EnvironmentObject var modelData: ModelData

@State private var selected = ModelData().landmarks

var popularLandmarks: [Landmark] {
    modelData.landmarks.filter { landmark in
        (landmark.popular)
    }
}

var rocommendedLandmarks: [Landmark] {
    modelData.landmarks.filter { landmark in
        (landmark.recommended)
    }
}

    var body: some View {
        VStack {
            Picker("Selection", selection: $selected) {
                Text("All").tag(modelData.landmarks)
                Text("Popular").tag(popularLandmarks)
                Text("Recommended").tag(rocommendedLandmarks)

            }
            .pickerStyle(SegmentedPickerStyle())
            .frame(width: UIScreen.main.bounds.width * 0.95)
            
            ForEach(selected, id: \.id) { land in
                NavigationLink(
                    destination: DetailView2(landmark: land)) {
                    MainItem3(landmark: land)
                    }
           }
        }
    }
}

Drop selected segment


Solution

  • selection: isn't going to work properly with an array type like this ([Landmark]).

    Instead, you'll probably want to do something simple like:

    @State private var selection = 0
    //...
    Picker("Selection", selection: $selection) {
       Text("All").tag(0)
       Text("Popular").tag(1)
       Text("Recommended").tag(2)
    

    And then use a computed property for your array that you iterate through later:

    var selected : [Landmark] {
      switch selection {
        case 0:
          return modelData.landmarks
        case 1:
          //etc
      }
    }
    

    You could do this with an enum : Int as well, which would probably be a little safer later on if you cases expand (and it would give you a limited set in your computed property).