Since you already have the visible region shown on the map and all annotations, with their coordinates, you can work out which annotations are located in that region.
MKMapRect has a useful method contains(_:) which indicates whether the specified map point lies within the rectangle. There are two (small?) problems though:
Map expects annotations to have location coordinates of type CLLocationCoordinate2D but MKMapRect uses MKMapPoint.
Luckily, an MKMapPoint can be formed from a CLLocationCoordinate2D.
I'm assuming you are storing your map's region as an MKCoordinationRegion object inside of an @State variable. If so, this needs to be converted to an MKMapRect object and that isn't an easy process. If you don't require using MKCoordinationRegion, then store an MKMapRect instead which will make things easier. If you do need it, you will need to manually convert (a quick search online will yield some results), and don't forget about the 180th meridian.
[Note: iOS 17 doesn't make Problem 2 an issue, with the new APIs handling things differently.]
Here's a demo app I made to showcase how you can achieve this:
// A single map annotation as an object
struct AnnotationItem: Identifiable {
let id = UUID()
let title: String
let coordinates: CLLocationCoordinate2D
}
struct MapAnnotations: View {
// Store the map's currently visible rect
@State private var visibleRect = MKMapRect(x: 125_000_000, y: 75_000_000, width: 15_000_000, height: 25_000_000)
// Dummy data
let items: [AnnotationItem] = [
.init(title: "San Francisco", coordinates: .init(latitude: 37.77938, longitude: -122.41843)),
.init(title: "New York", coordinates: .init(latitude: 40.71298, longitude: -74.00720)),
.init(title: "São Paulo", coordinates: .init(latitude: -23.57964, longitude: -46.65506)),
.init(title: "London", coordinates: .init(latitude: 51.50335, longitude: -0.07940)),
.init(title: "Rome", coordinates: .init(latitude: 41.88929, longitude: 12.49355)),
.init(title: "Johannesburg", coordinates: .init(latitude: -26.20227, longitude: 28.04363)),
.init(title: "Mumbai", coordinates: .init(latitude: 18.94010, longitude: 72.83466)),
.init(title: "Tokyo", coordinates: .init(latitude: 35.68951, longitude: 139.69170)),
.init(title: "Melbourne", coordinates: .init(latitude: -37.81503, longitude: 144.96634))
]
var body: some View {
Map(mapRect: $visibleRect, annotationItems: items) { item in
MapMarker(coordinate: item.coordinates)
}
.ignoresSafeArea()
.overlay(alignment: .bottom) {
let annotations = visibleAnnotations()
// Show the list of visible annotations if there are any
if !annotations.isEmpty {
VStack {
ForEach(annotations) { item in
Text(item.title)
.font(.title3.bold())
}
}
.padding(10)
.background(.thinMaterial, in: RoundedRectangle(cornerRadius: 10))
.padding()
}
}
}
func visibleAnnotations() -> [AnnotationItem] {
items.filter { item in
// Check if the item's location is in the map's currently visible rect
visibleRect.contains(.init(item.coordinates))
}
}
}
Any questions about this please let me know and I'll be happy to answer.