How to change displayPriority of MKUserLocation annotation in MapView? - ios

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.

Related

How to get rid of the MKBalloonCalloutView in iOS 14

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
}

How to show custom annotation even if it's on the user's current location with Swift

I've tow UIViewControllers the first one displays an MKMapKitView with annotations and the user's current location and in the second viewController I'll be constructing the annotations that will be displayed to the first viewController.
I found out that if the user's location and the annotation are the same the mapView will only display the user's current location.
Here's my code to create an annotation:
func addAnnotiation(for locationCoordinate: CLLocationCoordinate2D) {
let annotationPoint = MKPointAnnotation()
annotationPoint.coordinate = locationCoordinate
mapView.addAnnotation(annotationPoint)
}
My MKMapViewDelegate method:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let identifier = "Annotation"
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier)
if annotationView == nil {
annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
annotationView!.canShowCallout = true
} else {
annotationView!.annotation = annotation
}
return annotationView
}
How can I solve this problem so if the annotation and the user's location are the same it will show the annotation also and not only the user's current location?
The problem was very simple. I somehow forgot to make mapView.delegate = self. Everything is working just fine right now!

How to change markerTintColor in MapKit without changing default location pin in Swift?

I have MKMarkerAnnotationView to change color of pins on my map.
func mapView(_ MapView:MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView?{
let view = MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: "pin")
view.markerTintColor = .blue
return view
}
But, when I start my app, marker of my defoult location changes to.
How can I change pin without changing this marker?
Code to view location is also simple
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
{
self.MapView.showsUserLocation = true
}
Thanks for answer! :)
You could check if the annotation is the user location like this:
func mapView(_ MapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation {
return nil
}
let view = MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: "pin")
view.markerTintColor = .blue
return view
}
In your method, check to see if the annotation object is an instance of MKUserLocation. If it is, return nil to keep the standard user location annotation view.
(The Docs for mapView(_:viewFor:) explain this.)

Changing colour of pin in MKmapView turns new pin image aswell as current location into old pin image?

Im trying to change the colour of one set of pins to blue and another set of pins to remain red. Ive followed a question on here that takes me to the point where the pins do change colour. But also my current location turns into a pin. Also the pins turn from the new logo, into the old logo (an actually image of a pin). Is there a way for the pins to remain like the new logo/image and also have my current location to be displayed as normal, i.e a blue pulsing dot. as well as changing one set of pins to blue while the rest remain red?
Please Help!
Below is my code:
class MyPointAnnotation : MKPointAnnotation {
var pinTintColor: UIColor?
}
class ViewController: UIViewController, MKMapViewDelegate {
#IBOutlet var map: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
map.delegate = self
let anx = MyPointAnnotation()
anx.coordinate = CLLocationCoordinate2D(latitude: -36.56795, longitude: 176.56432)
anx.pinTintColor = .red
let any = MyPointAnnotation()
any.coordinate = CLLocationCoordinate2D(latitude: -36.54322, longitude: 176.43221)
any.pinTintColor = .blue
map.addAnnotation(anx)
map.addAnnotation(any)
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "myAnnotation") as? MKPinAnnotationView
if annotationView == nil {
annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "myAnnotation")
} else {
annotationView?.annotation = annotation
}
if let annotation = annotation as? MyPointAnnotation {
annotationView?.pinTintColor = annotation.pinTintColor
}
return annotationView
}
}
But also my current location turns into a pin.
Add the following code in mapView:viewFor... method :
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation {
return nil
}

Swift MapKit custom current location marker

This is what my project currently looks like. My questions is, how do I change the blue ball (current location) to a custom image or icon?
I am sure you know that a user is used to seeing that blue-dot as the current user's location. You shouldn't change it unless you have a good reason.
Here is how to change it:
Set the delegate for the mapView, and then override the following function... something like this:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation {
let pin = mapView.view(for: annotation) as? MKPinAnnotationView ?? MKPinAnnotationView(annotation: annotation, reuseIdentifier: nil)
pin.pinTintColor = UIColor.purple
return pin
} else {
// handle other annotations
}
return nil
}
and to have an image displayed instead:
Just replace the code inside if statement with the following code:
let pin = mapView.view(for: annotation) as? MKPinAnnotationView ?? MKPinAnnotationView(annotation: annotation, reuseIdentifier: nil)
pin.image = UIImage(named: "user_location_pin")
return pin
I think this code sample should give you enough information to help you figure out what to do. (Note that mapView is created in a storyboard...)
import UIKit
import MapKit
import CoreLocation
class ViewController: UIViewController, MKMapViewDelegate {
#IBOutlet weak var mapView: MKMapView!
let loc = CLLocationManager()
var angle = 0
var timer: NSTimer!
var userPinView: MKAnnotationView!
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self
loc.requestWhenInUseAuthorization()
timer = NSTimer.scheduledTimerWithTimeInterval(0.3, target: self, selector: #selector(rotateMe), userInfo: nil, repeats: true)
}
func rotateMe() {
angle = angle + 10
userPinView?.transform = CGAffineTransformMakeRotation( CGFloat( (Double(angle) / 360.0) * M_PI ) )
}
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation {
let pin = mapView.viewForAnnotation(annotation) ?? MKAnnotationView(annotation: annotation, reuseIdentifier: nil)
pin.image = UIImage(named: "userPinImage")
userPinView = pin
return pin
} else {
// handle other annotations
}
return nil
}
}
You can customise the view using MKMapDelegate's method:
optional func mapView(_ mapView: MKMapView,
viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView?
Parameters
mapView - The map view that requested the annotation view.
annotation - The object representing the annotation that is about to be displayed. In addition to your custom annotations, this object
could be an MKUserLocation object representing the user’s current
location.
See full the documentation here
Also please see the following SO question for updating the view when user location changes:
Custom Annotation view for userlocation not moving the mapview

Resources