0

I have imported JSON for countries:

Countries.json (sample)

    [ 
       {
          display_name: "France",
          timezone: "placeholder",
          longitude: 13.33,
          latitude: 15.34
       },   
       {
          display_name: "California",
          timezone: "EST",
          longitude: 33.33,
          latitude: 12.34
       }, 
  ]

I have a function getAnnotated that iterates through the countries to make an array of AnnotatedItem. That is used in Map and gets looped through as item to actually create the MapAnnotation. Then item is passed into a helper function getCountry. I filter through countries to get the country that has the same display_name field as item.

The desired behavior is to have an annotation/marker over each country and tapping on that annotation will pop up a modal/sheet that gives info on the country.
My issue is that if I am zoomed in and on screen is only a single annotation/marker, the proper country is displayed when clicking on it.
If I zoom out on the map and there are multiples annotations, every annotation I tap pops up the same country info for each one. I assume there is something wrong with the way I am looping.

var countries = Bundle.main.decode("Countries.json")

struct AnnotatedItem: Identifiable {
    let id = UUID()
    var name: String
    var coordinate: CLLocationCoordinate2D
}

struct MapView: View {
    @State var showSheet = false
    
    @State private var region = MKCoordinateRegion(
        center: CLLocationCoordinate2D(
            latitude: 25.7617,
            longitude: 80.1918
        ),
        span: MKCoordinateSpan(
            latitudeDelta: 10,
            longitudeDelta: 10
        )
    )
    
    func getAnnotated() -> [AnnotatedItem] {
        var pointsOfInterest = [AnnotatedItem]()

        for i in countries {
            pointsOfInterest.append(AnnotatedItem(name: i.display_name, coordinate: .init(latitude: i.latitude, longitude: i.longitude)))
        }
        
        return pointsOfInterest
    }

    func getCountry(newItem: AnnotatedItem) -> Country {
        let country = countries.filter{ $0.display_name == newItem.name }
        return country[0]
    }
    
    var body: some View {
        Map(coordinateRegion: $region, annotationItems: getAnnotated()) { item in
            MapAnnotation(coordinate: item.coordinate) {
                Button(action: {
                    showSheet.toggle()
                }){
                    Image(systemName: "airplane")
                        .foregroundColor(.white)
                        .padding()
                }
                
                .background(Circle())
                .foregroundColor(Color.green)
                .sheet(isPresented: $showSheet) {
                    SheetView(country: getCountry(newItem: item))
                }
                
            }
        }
    }

}

Ken White
  • 123,280
  • 14
  • 225
  • 444

1 Answers1

1

I would try something like this to achieve the desired behaviour:

class SelectedCountry: ObservableObject {
    @Published var item: AnnotatedItem = AnnotatedItem(name: "no name", coordinate: CLLocationCoordinate2D())
}

struct MapView: View {
    @ObservedObject var selected = SelectedCountry()  // <--- here
    @State var showSheet = false
    
    ...
    

    var body: some View {
        Map(coordinateRegion: $region, annotationItems: getAnnotated()) { item in
            MapAnnotation(coordinate: item.coordinate) {
                Button(action: {
                    selected.item = item  // <--- here
                    showSheet.toggle()
                }){
                    Image(systemName: "airplane")
                        .foregroundColor(.white)
                        .padding()
                }
                .background(Circle())
                .foregroundColor(Color.green)
            }
        }
        // ---> put the sheet here 
        .sheet(isPresented: $showSheet) {
            SheetView(country: getCountry(newItem: selected.item))
        }
    }
  • whoa, thanks! This works now. Is there any chance you could let me know what I was doing wrong (only if you have the time) ? I see the changes you made but I don't quite understand what made my code not work as expected. Regardless, thanks very much!! – mitchellOcavoos Jun 04 '21 at 20:47
  • 1
    the problem as I saw it, was to do with ".sheet(...)", you need to put this outside the loop. To do that you need to "capture" the item, and this is what the ObservableObject does. – workingdog support Ukraine Jun 04 '21 at 23:32