Hi all,
Xcode versIon: Version 16.2
Mac OS: Sequoia 15.3.2
Simulator: iPhone 16 Pro
Sharing this code revision in case others experience the same issue I did (took me an hour trying to web search a solution). After completing the lesson, the user location (blue dot) appeared in the Map but the business landmark pins disappeared. Commenting out // mapView.delegate = context.coordinator (while keeping the rest of the code), resulted in the business pins showing up again, but no callouts appeared when I clicked on a specific pin.
I had Claude.ai troubleshoot and implemented two recommendations see below. The explanation claude gave was:
"The reason your pins aren’t showing when you include mapView.delegate = context.coordinator
is because of how you’re implementing the annotation views. When you set a delegate, the system calls your mapView(_:viewFor:)
method for each annotation, but your current implementation isn’t properly configuring the annotation views.
The key changes I made:
- Used
MKMarkerAnnotationView
instead ofMKAnnotationView
:
MKMarkerAnnotationView
is specifically designed for displaying pins with callouts, making it more appropriate for your use case.- It automatically handles the pin appearance and callout display.
- Fixed the annotation management in
updateUIView
:
- Changed the approach to only remove non-user location annotations.
- Used
addAnnotations
instead ofshowAnnotations
for adding the business locations. - Called
showAnnotations
after adding the annotations to ensure all pins (including user location) are visible.
- Added proper type casting in the delegate method:
- Cast the dequeued view to
MKMarkerAnnotationView
. - Added configuration for the marker color.
"
Changes I made:
- func updateUIView(…),
replaced:
// Remove all annotations
uiView.removeAnnotations(uiView.annotations)
// Add the ones based on the business
uiView.showAnnotations(self.businessLocations, animated: true)
with:
// Remove all annotations except user location
let existingAnnotations = uiView.annotations.filter { !($0 is MKUserLocation) }
uiView.removeAnnotations(existingAnnotations)
// Add the business annotations
uiView.addAnnotations(self.businessLocations)
// Make sure all annotations are visible, including user location
if !self.businessLocations.isEmpty {
uiView.showAnnotations(uiView.annotations, animated: true)
}
- func mapView(…)
replaced:
// Check if there's a reusable annotation view
var annotationView = mapView.dequeueReusableAnnotationView(
withIdentifier: Constants.annotationReuseId)
with:
// Create a new annotation view & try to dequeue a reusable annotation view
var annotationView = mapView.dequeueReusableAnnotationView(
withIdentifier: Constants.annotationReuseId
) as? MKMarkerAnnotationView
and replaced:
// Create a new annotation view
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: Constants.annotationReuseId)
with:
// Create a new marker annotation view if none available for reuse
annotationView = MKMarkerAnnotationView(
annotation: annotation,
reuseIdentifier: Constants
.annotationReuseId)
Hope this helps save you some troubleshooting time.
//
// BusinessMap.swift
// CitySights App
//
// Created by Prash on 3/23/25.
//
import SwiftUI
import MapKit // this is a UIKit control (not a SwiftUI)
struct BusinessMap: UIViewRepresentable {
@EnvironmentObject var model: ContentModel
var businessLocations:[MKAnnotation] {
var annotations = [MKAnnotation]()
// Create set of annotations from our list of businesses
for business in model.restaurants + model.sights {
// if biz has lat and long, create MKPointAnnotation for it. Below, if business.coordinates.latitude is not nil, it gets assigned to lat; same for long
if let lat = business.coordinates?.latitude, let long = business.coordinates?.longitude {
// Create new annotation
let a = MKPointAnnotation()
a.coordinate = CLLocationCoordinate2D(latitude: lat, longitude: long)
a.title = business.name ?? ""
annotations.append(a)
} // if let
} // for
return annotations
} // var businessLocations
func makeUIView(context: Context) -> MKMapView {
let mapView = MKMapView()
// lets system handle creation of new coordinator or use existing coordinator
mapView.delegate = context.coordinator
// Show user locaiton the map
mapView.showsUserLocation = true
// rotates when user changes directions
mapView.userTrackingMode = .followWithHeading
return mapView
}
func updateUIView(_ uiView: MKMapView, context: Context) {
// going to called several times when Map is called
// CLAUDE: Remove all annotations except user location
let existingAnnotations = uiView.annotations.filter { !($0 is MKUserLocation) }
uiView.removeAnnotations(existingAnnotations)
// Add the business annotations
uiView.addAnnotations(self.businessLocations)
// Make sure all annotations are visible, including user location
if !self.businessLocations.isEmpty {
uiView.showAnnotations(uiView.annotations, animated: true)
}
// END OF CLAUDE
/*
// Remove all annotations
uiView.removeAnnotations(uiView.annotations)
// Add the ones based on the business
uiView.showAnnotations(self.businessLocations, animated: true)
*/
}
static func dismantleUIView(_ uiView: MKMapView, coordinator: Coordinator) {
// cleans up UIView when it's no longer needed
uiView.removeAnnotations(uiView.annotations)
}
// MARK: COORDINATOR CLASS
func makeCoordinator() -> Coordinator {
// return new Coordinator instance
return Coordinator(map: self)
}
// remember we're declaring this inside Struct
class Coordinator: NSObject, MKMapViewDelegate {
var map: BusinessMap
init(map: BusinessMap) {
self.map = map
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
// If annotation is user location represented by blue dot on map, return nil
if annotation is MKUserLocation {
return nil
}
// CLAUDE: Create a new annotation view & try to dequeue a reusable annotation view
var annotationView = mapView.dequeueReusableAnnotationView(
withIdentifier: Constants.annotationReuseId
) as? MKMarkerAnnotationView
/* Chris's code
// Check if there's a reusable annotation view
var annotationView = mapView.dequeueReusableAnnotationView(
withIdentifier: Constants.annotationReuseId)
*/
if annotationView == nil {
// CLAUDE: Create a new marker annotation view if none available for reuse
annotationView = MKMarkerAnnotationView(
annotation: annotation,
reuseIdentifier: Constants
.annotationReuseId)
// END CLAUDE
/*
// Create a new annotation view
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: Constants.annotationReuseId)
*/
// allows you to display external info in call out bubble
annotationView?.canShowCallout = true
annotationView?.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
annotationView?.markerTintColor = .red
} else {
// we have a resuable annotationView
annotationView?.annotation = annotation
}
// return it
return annotationView
} // func
} // class
} // struct