MKMapview delegate never called even with set delegate - ios

I have a UIView that integrate a scrollview. Inside the scrollview I have a map where a user need to move a pin. I'm gonna reuse the location of pin later.
I'm setting strong reference and setting delegate ( both design and code) for the map. I'm able to see the pin created but to have custom icon and possibility to set it draggable, I have implement the mapView( viewFor MKAnnotation
This seems to be never called and I'm not getting why.
I also try to get another delegate as didfinishloading to understand if problem is related to the mkannotation but also this one is never been called.
import Foundation
import Firebase
import MapKit
import UIKit
public class Setup: UIViewController,CLLocationManagerDelegate,MKMapViewDelegate{
var ChargePointID = ""
#IBOutlet weak var chargepointIDLabel: UILabel!
let locationManager = CLLocationManager()
var pin: customPin!
//setting up variables
//pricing textfield and slider
#IBOutlet weak var priceLabel: UITextField!
#IBOutlet weak var priceSlider: UISlider!
#IBOutlet var map: MKMapView!
override public func viewDidLoad() {
super.viewDidLoad()
chargepointIDLabel.text = ChargePointID
self.map.delegate = self
self.map.mapType = .hybrid
// Ask for Authorisation from the User.
self.locationManager.requestAlwaysAuthorization()
// For use in foreground
self.locationManager.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.startUpdatingLocation()
}
//create liocation relative to user
let location = CLLocationCoordinate2D(latitude: (locationManager.location?.coordinate.latitude)!, longitude: (locationManager.location?.coordinate.longitude)!)
//centering map to user location
let region = MKCoordinateRegion(center: location, span: MKCoordinateSpan(latitudeDelta: 0.005, longitudeDelta: 0.005))
self.map.setRegion(region, animated: true)
//creating a pin of type custompin
pin = customPin(pinTitle: ChargePointID, pinSubTitle: "", location: location)
map.addAnnotation(pin)
}
private func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let locValue: CLLocationCoordinate2D = manager.location?.coordinate else { return }
print("locations = \(locValue.latitude) \(locValue.longitude)")
}
private func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
print("annotation title == \(String(describing: view.annotation?.title!))")
}
private func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
print("ok")
let annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: "customannotation")
annotationView.image = UIImage(named:"setuppin")
annotationView.canShowCallout = true
return annotationView
}
func mapViewDidFinishLoadingMap(_ mapView: MKMapView) {
print(" map has finished")
}
private func map(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
print("ok")
let annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: "customannotation")
annotationView.image = UIImage(named:"setuppin")
annotationView.canShowCallout = true
return annotationView
}
}
class customPin: NSObject, MKAnnotation {
var coordinate: CLLocationCoordinate2D
var title: String?
var subtitle: String?
init(pinTitle:String, pinSubTitle:String, location:CLLocationCoordinate2D) {
self.title = pinTitle
self.subtitle = pinSubTitle
self.coordinate = location
}
}

private is not necessary. Replace
private func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
with
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {

this was cause Xcode was raising this warning
instance method 'mapView(:viewFor:)' nearly matches optional requirement 'mapView(:viewFor:)' of protocol 'MKMapViewDelegate'
and suggest make it private as fix. btw even without private not raise anything

DAMN!!!!!
I solved it.... the warning was cause my class was declared ad public and delegate need to be too.
I removed the public statement from my class and everything start to work .
so problem in end was main class declared as public !

Related

Custom clustered annotations on MapKit crash when rapidly dragging the map or changing the zoom-level

After implementing a way to cluster customised annotations, the application crashes whenever the view of the map is adjusted rapidly, by scrolling or changing the zoom-level.
-[MKPointAnnotation memberAnnotations]: unrecognized selector sent to instance 0x281396c00
My guess is that the compiler is trying to retrieve the annotation information, but cannot find the data. As I'm fairly new to Swift, I don't see what I'm missing. Your help would be greatly appreciated.
I have a pretty basic setup to display the map in SwiftUI. In the main file, I call the MapView from MapView.swift
struct MapView: UIViewRepresentable {
#ObservedObject var store = DataStoreMap()
func makeCoordinator() -> MapViewCoordinator {
MapViewCoordinator(self)
}
func makeUIView(context: Context) -> MKMapView{
MKMapView(frame: .zero)
}
func updateUIView(_ view: MKMapView, context: Context){
let location = getUserLocation()
let chargers = store.chargers
let coordinate = CLLocationCoordinate2D(latitude: location.latitude, longitude: location.longitude)
let span = MKCoordinateSpan(latitudeDelta: 0.03, longitudeDelta: 0.03)
let region = MKCoordinateRegion(center: coordinate, span: span)
view.setRegion(region, animated: true)
for charger in chargers {
let annotation = MKPointAnnotation()
annotation.coordinate = CLLocationCoordinate2D(latitude: charger.addressInfo.latitude, longitude: charger.addressInfo.longitude)
view.delegate = context.coordinator
view.addAnnotation(annotation)
}
}
}
Also included in the same file is my custom annotation class.
class MapViewCoordinator: NSObject, MKMapViewDelegate {
var mapViewController: MapView
init(_ control: MapView) {
self.mapViewController = control
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
//Custom View for Annotation
var annotationView = MKMarkerAnnotationView()
annotationView.canShowCallout = true
let identifier = "laadpaal"
if let dequedView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier) as? MKMarkerAnnotationView {
annotationView = dequedView
} else {
annotationView = MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: identifier)
}
annotationView.markerTintColor = .some(.systemBlue)
annotationView.glyphImage = UIImage(named: "car1")
annotationView.glyphTintColor = .yellow
annotationView.clusteringIdentifier = identifier
return annotationView
}
}
The cause for your crash is that you don't account for other annotations requested by map kit (e.g. MKUserLocation). You are triggering this due to the automatic clustering as you set clusteringIdentifier to a non-nil value.
Just return nil when you want to deal with the annotation yourself so MKMapView uses the default handling:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation { return nil }
if annotation is MKClusterAnnotation { return nil }
let view = mapView.dequeueReusableAnnotationView(withIdentifier: "identifier", for: annotation)
view.clusteringIdentifier = "clusterIdentifer"
// …
return view
}
If you ever want to customize the cluster annotations just add a special case for MKClusterAnnotation. And if you show user location don't forget to return nil for MKUserLocation if you want the default blue dot.

How to change displayPriority of MKUserLocation annotation in MapView?

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.

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
}

ColorPointAnnotation for map kit in swift 3 not working [duplicate]

I have 2 pins in mapkit, both are under the same annotation view so it makes since that both of the pins are the same color. How can I make the pins different colors. I would like hello to be red and hellox to be blue.
import UIKit
import MapKit
class ViewController: UIViewController, MKMapViewDelegate {
#IBOutlet var jmap: MKMapView!
override func viewDidLoad() {
jmap.delegate = self;
let hello = MKPointAnnotation()
hello.coordinate = CLLocationCoordinate2D(latitude: 40, longitude: -73)
jmap.addAnnotation(hello)
let hellox = MKPointAnnotation()
hellox.coordinate = CLLocationCoordinate2D(latitude: 34, longitude: -72)
jmap.addAnnotation(hellox)
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let annotationView = MKPinAnnotationView()
annotationView.pinTintColor = .blue
return annotationView
}}
Subclass MKPointAnnotation to add any custom property that you want, such as a pinTintColor:
class MyPointAnnotation : MKPointAnnotation {
var pinTintColor: UIColor?
}
class ViewController: UIViewController, MKMapViewDelegate {
#IBOutlet var jmap: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
jmap.delegate = self
let hello = MyPointAnnotation()
hello.coordinate = CLLocationCoordinate2D(latitude: 40, longitude: -73)
hello.pinTintColor = .red
let hellox = MyPointAnnotation()
hellox.coordinate = CLLocationCoordinate2D(latitude: 34, longitude: -72)
hellox.pinTintColor = .blue
jmap.addAnnotation(hello)
jmap.addAnnotation(hellox)
}
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
}
}

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