How do I disable the "You are here" callout attached to the user location annotation in Mapbox in Swift?
You need to implement the following method which checks for the user location annotation.
func mapView(_ mapView: MGLMapView, annotationCanShowCallout annotation: MGLAnnotation) -> Bool {
if annotation is MGLUserLocation {
return false
} else {
return true
}
}
Related
I have the following code which works for iOS 13 and lower.
func mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation) {
mapView.userLocation.title = "You are here"
mapView.userLocation.subtitle = // user's location
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation.isKind(of: MKUserLocation.self) {
return nil
}
}
It shows only the blue dot without a callout and above the blue dot is just the title and subtitle.
But on iOS 14 there is a default MKBalloonCalloutView that appears in place of the title and subtitle. It shows a gray profileImage. How can I get rid of the BalloonCallout so I can show just the title and subtitle?
By setting your own detail detailCalloutAccessoryView for the User Location annotation's MKAnnotationView the behaviour reverts to just showing title and subtitle.
You can set any UIView of your choice, like an UIImageView for example, or just an empty one.
For example in your MKMapViewDelegate
func mapViewDidFinishLoadingMap(_ mapView: MKMapView) {
mapView.view(for: mapView.userLocation)?.detailCalloutAccessoryView = .init()
}
For the user (the one from the question), if using iOS 14 and higher I use MKMarkerAnnotationView,. If using iOS 13 or lower I use MKPinAnnotationView. I have a separate custom pin for everyone else:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let reuseIdentifier = "MyIdentifier"
if annotation.isKind(of: MKUserLocation.self) {
if #available(iOS 14.0, *) {
if let pin = mapView.dequeueReusableAnnotationView(withIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier) as? MKMarkerAnnotationView {
return setMKMarkerAnnotationView(pin: pin)
}
} else {
let pin = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseIdentifier)
pin.canShowCallout = true
return pin
}
return nil
} else {
// dequeue the custom pins for everyone else
}
}
func setMKMarkerAnnotationView(pin: MKMarkerAnnotationView) -> MKMarkerAnnotationView {
pin.animatesWhenAdded = true
pin.markerTintColor = UIColor.red
pin.titleVisibility = .visible
pin.subtitleVisibility = .visible
return pin
}
I have a MapView displaying some annotations with displayPriority = .defaultHight to allow automatic clustering.
The MapView also displays the current user location which has a default display priority of required.
This causes my annotations to be hidden by the user location annotation when they are very close together.
I want to change this behavior by setting the display priority of the user location annotation to defaultLow.
I tried using this approach:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation {
let userView = mapView.view(for: annotation)
userView?.displayPriority = .defaultLow
return userView
}
return mapView.view(for: annotation)
}
However userView is always nil and therefore my displayPriority modification is not applied.
Any ideas how the displayPriority of the MKUserLocation annotation view can be changed?
I spent hours trying to solve this problem by customizing the default user location annotation, but to no avail.
Instead, as a workaround, I made my own location marker and hid the default location annotation. Here's my code:
Add an annotaion variable to your viewController:
private var userLocation: MKPointAnnotation?
In your viewDidLoad, hide the default location marker:
mapView.showsUserLocation = false
Update the location in didUpdateLocations:
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let userLocation = locations.first else { return }
if self.userLocation == nil {
let location = MKPointAnnotation()
location.title = "My Location"
location.coordinate = userLocation.coordinate
mapView.addAnnotation(location)
self.userLocation = location
} else {
self.userLocation?.coordinate = userLocation.coordinate
}
}
Then customize the annotation view in viewFor annotation:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
// user location annotation
let identifier = "userLocation"
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier)
if annotationView == nil {
annotationView = MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: identifier)
(annotationView as? MKMarkerAnnotationView)?.markerTintColor = .blue
annotationView?.canShowCallout = true
} else {
annotationView?.annotation = annotation
}
annotationView?.displayPriority = .defaultLow
return annotationView
}
I changed the annotation's displayPriority to .defaultLow to make sure it won't hide other annotations.
Let me know if this helps!
In case anyone is still struggling with this, you can do this using func mapView(_ mapView: MKMapView, didAdd views: [MKAnnotationView]):
// MARK: - MKMapViewDelegate
func mapView(_ mapView: MKMapView, didAdd views: [MKAnnotationView]) {
for view in views {
if view.annotation is MKUserLocation {
view.displayPriority = .defaultLow
break
}
}
}
This way you can still use the system provided view for MKUserLocation without having to construct your own manually.
I'm trying to get the MKAnnotationView that displays the user location (blue dot) in MapKit to add a custom gesture recognizer. Is this possible?
I've tried via the delegate method but I don't know how to dequeue the view when I don't have the identifier string. If I had the identifier I could probably do something like the code below;
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation {
let userLocationAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "??", for: annotation)
userLocationAnnotationView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.handleStuff)))
return userLocationAnnotationView
}
}
EDIT
Ah, been stuck on this for hours and found the solution directly after I posted. I found two different ways of solving it with delegate methods. The first way could be via didSelect:
func mapView(_ mapView: MKMapView,
didSelect view: MKAnnotationView){
if view.annotation is MKUserLocation {
// User clicked on the blue dot
}
}
Or if a gesture recognizer is needed it could be done as below. Should probably add a check if the gesture recognizer already have been added since this function gets called every time an annotation view is added.
func mapView(_ mapView: MKMapView,
didAdd views: [MKAnnotationView]){
for view in views {
if view.annotation is MKUserLocation {
view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.handleStuff)))
}
}
}
I am currently using the MapBox swift library and I am trying to add a delegate to the callout view.
I am trying to do it like this :
func mapView(_ mapView: MGLMapView, calloutViewFor annotation: MGLAnnotation) -> MGLCalloutView? {
let callout = TabletMapSearchCalloutView(representedObject: annotation, mapView: mapView)
callout.delegate = self
return callout
}
But it seems that there is an internal worker that runs in the library that sets the delegate after this method to the MGLMapView.
So my question is, how do I go about adding a delegate to the callout so that I can access the tapped event?
So it seems the MGLMapViewDelegate has the method that I was looking for
func mapView(_ mapView: MGLMapView, tapOnCalloutFor annotation: MGLAnnotation)
I have set up a simple app with a tabbed map view (map on tab 1) that reads info from a json file on a server (point info is stored in MySQL) with SwiftyJSON and Alamofire. The points are loaded into the map. What I would like to do is add a button to the annotation, which outlets to a different view tab and passes along the point's ID.
I have not tried anything yet as I have no idea where to get started and the documentation doesn't mention anything similar.
How does one go about adding an outlet to a map point annotation?
When you tap your pin the callout appears with a title and/or a subtitle. You need to add left and right callout accessory views to this callout. You do this by conforming to the MGLMapViewDelegate and implementing the following methods:
func mapView(_ mapView: MGLMapView, annotationCanShowCallout annotation: MGLAnnotation) -> Bool {
return true
}
func mapView(_ mapView: MGLMapView, leftCalloutAccessoryViewFor annotation: MGLAnnotation) -> UIView? {
...your code here...
let deleteButton = UIButton(type: .custom)
deleteButton.tag = 100
}
func mapView(_ mapView: MGLMapView, rightCalloutAccessoryViewFor annotation: MGLAnnotation) -> UIView? {
...your code here...
let infoButton = UIButton(type: .custom)
infoButton.tag = 101
}
func mapView(_ mapView: MGLMapView, annotation: MGLAnnotation, calloutAccessoryControlTapped control: UIControl) {
...your code here...
switch control.tag {
case 100:
// do some delete stuff
case 101:
// do some info stuff
default:
break
}
The MapBox example showing actual usage is here:
MapBox example. This example doesn't segue but you would just trigger a segue with the button tap instead of the UIAlert.
For future reference:
func mapView(_ mapView: MGLMapView, annotation: MGLAnnotation, calloutAccessoryControlTapped control: UIControl) {
// Hide the callout view.
// mapView.deselectAnnotation(annotation, animated: true)
performSegue(withIdentifier: "toDetail", sender: view)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
print("Seque toDetail")
// do stuff
}