I would like to have the users location as a blue dot while having pins also on the mapview. I would like the pins to have an annotation that has a an info button.
I can get the blue dot for users location and the enable the pins to have an annotation such as a title an subtitle. However when I go to add the info button to the red pins the users location (blue dot) turns into a red pin.
I can't seem to spot where I'm going wrong. It has something to do with the last function because this is the function that puts a the info button onto the annotation. But it also selects the users current location and turns it into a pin for some reason :(
class GetToTheStart: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
#IBOutlet weak var mapView: MKMapView!
let myLocMgr = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
myLocMgr.desiredAccuracy = kCLLocationAccuracyBest
myLocMgr.requestWhenInUseAuthorization()
myLocMgr.startUpdatingLocation()
myLocMgr.delegate = self
mapView.delegate = self
var zoo = CLLocationCoordinate2DMake(53.3562, -6.3053)
var zoopin = MKPointAnnotation()
zoopin.coordinate = zoo
zoopin.title = "dublin zoo"
zoopin.subtitle = "hello this is the zoo"
mapView.addAnnotation(zoopin)
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// get most recient coordinate
let myCoor = locations[locations.count - 1]
//get lat & long
let myLat = myCoor.coordinate.latitude
let myLong = myCoor.coordinate.longitude
let myCoor2D = CLLocationCoordinate2D(latitude: myLat, longitude: myLong)
//set span
let myLatDelta = 0.05
let myLongDelta = 0.05
let mySpan = MKCoordinateSpan(latitudeDelta: myLatDelta, longitudeDelta: myLongDelta)
let myRegion = MKCoordinateRegion(center: myCoor2D, span: mySpan)
self.mapView.showsUserLocation = true
}
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
let reuseIdentifier = "pin"
var pin = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseIdentifier) as? MKPinAnnotationView
if pin == nil {
pin = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseIdentifier)
pin!.pinColor = .Red
pin!.canShowCallout = true
pin!.rightCalloutAccessoryView = UIButton(type: .DetailDisclosure)
} else {
pin!.annotation = annotation
}
return pin
}
viewForAnnotation delegate method will be called by all the annotation on the map including userLocation.
So you just check and return nil for it as shown below...
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation {
//return nil so map view draws "blue dot" for standard user location
return nil
}
let reuseIdentifier = "pin"
var pin = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseIdentifier) as? MKPinAnnotationView
if pin == nil {
pin = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseIdentifier)
pin!.pinColor = .Red
pin!.canShowCallout = true
pin!.rightCalloutAccessoryView = UIButton(type: .DetailDisclosure)
} else {
pin!.annotation = annotation
}
return pin
}
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
I am having some trouble getting a custom annotation to load inside of my map view when I try to place a pin.
import UIKit
import MapKit
import CoreLocation
class MapViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate{
#IBAction func ReportBtn(sender: AnyObject) {
//MARK: Report Date And Time Details
let ReportTime = NSDate()
let TimeStamp = NSDateFormatter()
TimeStamp.timeStyle = NSDateFormatterStyle.ShortStyle
TimeStamp.dateStyle = NSDateFormatterStyle.ShortStyle
TimeStamp.stringFromDate(ReportTime)
//MARK: Default Point Annotation Begins
let ReportAnnotation = MKPointAnnotation()
ReportAnnotation.title = "Annotation Created"
ReportAnnotation.subtitle = ReportTime.description
ReportAnnotation.coordinate = locationManager.location!.coordinate
mapView(MainMap, viewForAnnotation: ReportAnnotation)
MainMap.addAnnotation(ReportAnnotation)
}
#IBOutlet weak var MainMap: MKMapView!
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.startUpdatingLocation()
self.MainMap.showsUserLocation = true
}
//MARK: - Location Delegate Methods
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations.last
let center = CLLocationCoordinate2D(latitude: location!.coordinate.latitude, longitude: location!.coordinate.longitude)
let region = MKCoordinateRegion(center: center, span: MKCoordinateSpan(latitudeDelta: 0.02, longitudeDelta: 0.02 ))
self.MainMap.setRegion(region, animated: true)
//self.locationManager.stopUpdatingLocation()
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError){
print(error.localizedDescription)
}
//MARK:Custom Annotation Begins Here
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
guard !annotation.isKindOfClass(MKUserLocation) else {
return nil
}
/*if annotation.isKindOfClass(MKUserLocation){
//emty return, guard wasn't cooperating
}else{
return nil
}*/
let annotationIdentifier = "AnnotationIdentifier"
var annotationView: MKAnnotationView?
if let dequeuedAnnotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(annotationIdentifier){
annotationView = dequeuedAnnotationView
annotationView?.annotation = annotation
}
else{
let av = MKAnnotationView(annotation: annotation, reuseIdentifier: annotationIdentifier)
av.rightCalloutAccessoryView = UIButton(type: .DetailDisclosure)
annotationView = av
}
if let annotationView = annotationView {
annotationView.canShowCallout = true
annotationView.image = UIImage(named: "image.png")
}
return annotationView
}
}
Added Information
I am positive that the button functionality works perfect. With the current code, dumped above, the default red pin annotation appears right where it should. When I tap on the pin, the description I specified also appears without an issue. The only problem I am having with this code is that I cannot get my image to take the place of the boring, default red pin
I recommend subclassing `MKPointAnnotation.
Pokémon Pin
I have included only the necessary code to display a custom map pin. Think of it as a template.
Outline
We will create a point annotation object and assigning a custom image name with the CustomPointAnnotation class.
We will subclass the MKPointAnnotation to set image and assign it on the delegate protocol method viewForAnnotation.
We will add an annotation view to the map after setting the coordinate of the point annotation with a title and a subtitle.
We will implement the viewForAnnotation method which is an MKMapViewDelegate protocol method which gets called for pins to display on the map. viewForAnnotation protocol method is the best place to customise the pin view and assign a custom image to it.
We will dequeue and return a reusable annotation for the given identifier and cast the annotation to our custom CustomPointAnnotation class in order to access the image name of the pin.
We will create a new image set in Assets.xcassets and place image#3x.png and image#2x.png accordingly.
Don't forget plist.
NSLocationAlwaysUsageDescription and NSLocationWhenInUseUsageDescription
As always test on a real device.
The swizzle 🌀
//1
CustomPointAnnotation.swift
import UIKit
import MapKit
class CustomPointAnnotation: MKPointAnnotation {
var pinCustomImageName:String!
}
//2
ViewController.swift
import UIKit
import MapKit
class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
#IBOutlet weak var pokemonMap: MKMapView!
let locationManager = CLLocationManager()
var pointAnnotation:CustomPointAnnotation!
var pinAnnotationView:MKPinAnnotationView!
override func viewDidLoad() {
super.viewDidLoad()
//Mark: - Authorization
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
pokemonMap.delegate = self
pokemonMap.mapType = MKMapType.Standard
pokemonMap.showsUserLocation = true
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = CLLocationCoordinate2D(latitude: 35.689949, longitude: 139.697576)
let center = location
let region = MKCoordinateRegionMake(center, MKCoordinateSpan(latitudeDelta: 0.025, longitudeDelta: 0.025))
pokemonMap.setRegion(region, animated: true)
pointAnnotation = CustomPointAnnotation()
pointAnnotation.pinCustomImageName = "Pokemon Pin"
pointAnnotation.coordinate = location
pointAnnotation.title = "POKéSTOP"
pointAnnotation.subtitle = "Pick up some Poké Balls"
pinAnnotationView = MKPinAnnotationView(annotation: pointAnnotation, reuseIdentifier: "pin")
pokemonMap.addAnnotation(pinAnnotationView.annotation!)
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
print(error.localizedDescription)
}
//MARK: - Custom Annotation
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
let reuseIdentifier = "pin"
var annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseIdentifier)
if annotationView == nil {
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseIdentifier)
annotationView?.canShowCallout = true
} else {
annotationView?.annotation = annotation
}
let customPointAnnotation = annotation as! CustomPointAnnotation
annotationView?.image = UIImage(named: customPointAnnotation.pinCustomImageName)
return annotationView
}
}
There are a few issues you need to deal with.
MKMapView and annotations
Firstly, it is necessary to understand how MKMapView displays an annotation view from an annotation. There are
MKMapView - displays the map and manages annotations.
MKMapViewDelegate - you return data from specific functions to MKMapView.
MKAnnotation - contains data about a location on the map.
MKAnnotationView - displays an annotation.
An MKAnnotation holds the data for a location on the map. You create this data and hand it to MKMapView. At some point in the future, when the map view is ready to display the annotation it will call back to the delegate and ask it to create an MKAnnotationView for an MKAnnotation. The delegate creates and returns the view and the map view displays it. You specify the delegate in the storyboard, or in code e.g. mapView.delegate = self.
Location
Tracking the users location is complicated by:
Permission is needed from the user before location tracking is enabled.
There is a delay after the user allows tracking, until the user's location is available.
Location services might not even be enabled.
Your code needs to deal with authorisation by checking CLLocationManager.authorizationStatus, and implementing CLLocationManagerDelegate methods.
Note that to use requestWhenInUseAuthorization requires entry for NSLocationWhenInUseUsageDescription in Info.plist
Example
Example project on GitHub.
import UIKit
import MapKit
import CoreLocation
class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
// Instance of location manager.
// This is is created in viewDidLoad() if location services are available.
var locationManager: CLLocationManager?
// Last location made available CoreLocation.
var currentLocation: MKUserLocation? {
didSet {
// Hide the button if no location is available.
button.hidden = (currentLocation == nil)
}
}
// Date formatter for formatting dates in annotations.
// We use a lazy instance so that it is only created when needed.
lazy var formatter: NSDateFormatter = {
let formatter = NSDateFormatter()
formatter.timeStyle = NSDateFormatterStyle.ShortStyle
formatter.dateStyle = NSDateFormatterStyle.ShortStyle
return formatter
}()
#IBOutlet var button: UIButton!
#IBOutlet var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self
// Track the user's location if location services are enabled.
if CLLocationManager.locationServicesEnabled() {
locationManager = CLLocationManager()
locationManager?.delegate = self
locationManager?.desiredAccuracy = kCLLocationAccuracyBest
switch CLLocationManager.authorizationStatus() {
case .AuthorizedAlways, .AuthorizedWhenInUse:
// Location services authorised.
// Start tracking the user.
locationManager?.startUpdatingLocation()
mapView.showsUserLocation = true
default:
// Request access for location services.
// This will call didChangeAuthorizationStatus on completion.
locationManager?.requestWhenInUseAuthorization()
}
}
}
//
// CLLocationManagerDelegate method
// Called by CLLocationManager when access to authorisation changes.
//
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
switch status {
case .AuthorizedAlways, .AuthorizedWhenInUse:
// Location services are authorised, track the user.
locationManager?.startUpdatingLocation()
mapView.showsUserLocation = true
case .Denied, .Restricted:
// Location services not authorised, stop tracking the user.
locationManager?.stopUpdatingLocation()
mapView.showsUserLocation = false
currentLocation = nil
default:
// Location services pending authorisation.
// Alert requesting access is visible at this point.
currentLocation = nil
}
}
//
// MKMapViewDelegate method
// Called when MKMapView updates the user's location.
//
func mapView(mapView: MKMapView, didUpdateUserLocation userLocation: MKUserLocation) {
currentLocation = userLocation
}
#IBAction func addButtonTapped(sender: AnyObject) {
guard let coordinate = currentLocation?.coordinate else {
return
}
let reportTime = NSDate()
let formattedTime = formatter.stringFromDate(reportTime)
let annotation = MKPointAnnotation()
annotation.title = "Annotation Created"
annotation.subtitle = formattedTime
annotation.coordinate = coordinate
mapView.addAnnotation(annotation)
}
//
// From Bhoomi's answer.
//
// MKMapViewDelegate method
// Called when the map view needs to display the annotation.
// E.g. If you drag the map so that the annotation goes offscreen, the annotation view will be recycled. When you drag the annotation back on screen this method will be called again to recreate the view for the annotation.
//
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
guard !annotation.isKindOfClass(MKUserLocation) else {
return nil
}
let annotationIdentifier = "AnnotationIdentifier"
var annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(annotationIdentifier)
if annotationView == nil {
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: annotationIdentifier)
annotationView!.rightCalloutAccessoryView = UIButton(type: .DetailDisclosure)
annotationView!.canShowCallout = true
}
else {
annotationView!.annotation = annotation
}
annotationView!.image = UIImage(named: "smile")
return annotationView
}
}
check your image.png in your project bundle or Assets.xcassets
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
guard !annotation.isKind(of: MKUserLocation.self) else {
return nil
}
let annotationIdentifier = "AnnotationIdentifier"
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: annotationIdentifier)
if annotationView == nil {
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: annotationIdentifier)
annotationView?.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
annotationView!.canShowCallout = true
}
else {
annotationView!.annotation = annotation
}
annotationView!.image = UIImage(named: "image.png")
return annotationView
}
Do as follow may be work for you.
1) Create custom class for the Annotation Pin.
class CustomPointAnnotation: MKPointAnnotation {
var imageName: UIImage!
}
2)Define variable as below.
var locationManager = CLLocationManager()
3) Call below method in viewDidLoad()
func checkLocationAuthorizationStatus() {
if CLLocationManager.authorizationStatus() == .AuthorizedAlways {
map.showsUserLocation = false
} else {
locationManager.requestWhenInUseAuthorization()
}
}
4) Put below code in viewWillAppear()
self.map.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
locationManager.delegate = self
dispatch_async(dispatch_get_main_queue(),{
self.locationManager.startUpdatingLocation()
})
5) Most important implement below method.
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
if !(annotation is CustomPointAnnotation) {
return nil
}
let reuseId = "Location"
var anView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId)
if anView == nil {
anView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
anView!.canShowCallout = true
}
else {
anView!.annotation = annotation
}
let cpa = annotation as! CustomPointAnnotation
anView!.image = cpa.imageName
return anView
}
6) Execute below code where you you have received custom pin image
let strLat = "YOUR LATITUDE"
let strLon = "YOUR LONGITUDE"
let info = CustomPointAnnotation()
info.coordinate = CLLocationCoordinate2DMake(strLat.toDouble()!,strLon.toDouble()!)
info.imageName = resizedImage
info.title = dict!["locationName"]! as? String
self.map.addAnnotation(info)
From the code and according to the MapKit guide, your code look correct. I am thinking that it could be this line annotationView.image = UIImage(named: "image.png")
Is there a chance that image.png could be the wrong image name or not added in to the project when compile? Also just fyi, if you are using .xcassets, you does not have to add a .png.
As annotationView.image is a optional, when the image UIImage(named: "image.png") is nil, it will not crash but just render the default pin image.
If this is not the issue, please provide more info on the debugging steps that you have taken so the rest of us can understand better and help you. Cheers =)
I'm following the MapKit tutorial on Ray Wenderlich's site and I'm a little confused on what a few lines of code are saying.
Specifically under the comment //4, it appears that the developer uses a constant variable that's equal to a function of the mapView and then casts it to a type of MKMarkAnnotationView.
I've never seen anything like this but I'd like to understand it before moving on. I understand that functions are objects too and I understand that it is possible to place a function inside a variable however in this example the developer not only places a function inside a variable, but the developer also casts it into a different type which is confusing. Can this line of code be broken down into smaller steps to help me understand it better?
It seems the developer called on the mapView object which is of type MKMapView but was allowed to optionally cast it to a type of MKMarkerAnnotationView.
//4
if let dequeuedView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier) as? MKMarkerAnnotationView {
dequeuedView.annotation = annotation
view = dequeuedView
}
Here is the viewController's code in its entirety if needed:
import UIKit
import MapKit
class ViewController: UIViewController {
//created an IBOutlet to control the mapView in interface builder
#IBOutlet weak var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
//initial location to zoom the map into once the app is opened.
let initialLocation = CLLocation.init(latitude: 21.282778, longitude: -157.829444)
centerMapOnLocation(location: initialLocation)
mapView.delegate = self
let artwork = Artwork.init(title: "King David Kalakaua", locationName: "Waikiki Gateway Park", discipline: "Sculpture", coordinate: CLLocationCoordinate2D.init(latitude: 21.283921, longitude: -157.831661))
mapView.addAnnotation(artwork)
}
//when specifying a latlong to zoom into in iOS, you must also state a rectangular region for it to display a correct zoom level???
let regionRadius: CLLocationDistance = 1000
func centerMapOnLocation(location: CLLocation){
let coordinateRegion = MKCoordinateRegionMakeWithDistance(location.coordinate, regionRadius, regionRadius)
mapView.setRegion(coordinateRegion, animated: true)
}
}
extension ViewController: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
//2
guard let annotation = annotation as? Artwork else {
return nil
}
//3
let identifier = "marker"
var view: MKMarkerAnnotationView
//4
if let dequeuedView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier) as? MKMarkerAnnotationView {
dequeuedView.annotation = annotation
view = dequeuedView
} else {
//5
view = MKMarkerAnnotationView.init(annotation: annotation, reuseIdentifier: identifier)
view.canShowCallout = true
view.calloutOffset = CGPoint.init(x: -5, y: 5)
view.rightCalloutAccessoryView = UIButton.init(type: .detailDisclosure)
view.markerTintColor = UIColor.green
}
return view
}
}
This is optional unwrapping.
As you noticed - developer optionally casted function's result to MKMarkerAnnotationView. But he also used this with if let syntax which is optional unwrapping. This means that this code
dequeuedView.annotation = annotation
view = dequeuedView
will only be executed if cast succeeded (i.e. if cast result wasn't nil). Otherwise this code will be ignored.
You can also do this with guard statement. E.g.:
guard let dequeuedView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier) as? MKMarkerAnnotationView
else { // code here will be executed if casting fails. In this case you also have to return function }
dequeuedView.annotation = annotation
view = dequeuedView
More info in documentation
I've written this function to add overlay at Loading View and its working perfectly.
How can I use this function or any other method to update overlay according to new coordinates I pass on a button click.
I'm passing an array of type CLLocationCoordinate2D of coordinates.
func drawpolyline(loclist: Array<CLLocationCoordinate2D>) {
// if let overlays = self.mainMapView?.overlays {
// self.mainMapView.removeOverlays(overlays)
// }
if (loclist != nil && loclist.count > 1) {
let startmark = loclist.first
let cord1 = CLLocationCoordinate2D(latitude: (startmark?.latitude)!, longitude: (startmark?.longitude)!)
let polyline = MKPolyline(coordinates: loclist, count: loclist.count)
print(polyline.pointCount)
let latDelta:CLLocationDegrees = 0.02
let lonDelta:CLLocationDegrees = 0.02
let mapSpan = MKCoordinateSpanMake(latDelta, lonDelta)
let mapRegion = MKCoordinateRegion(center: cord1, span: mapSpan)
// let adjustedRegion: MKCoordinateRegion = [self.mainMapView regionThatFits:viewRegion]
if (mainMapView != nil) {
self.mainMapView.delegate = self
self.mainMapView.add(polyline)
self.mainMapView.setRegion(mapRegion, animated: true);
// self.mainMapView.addAnnotation(mapAnnotation);
}
} else {
configureMap()
}
}
//function of MKMapViewDelegate for making overlays
//Function to give polyline color and width etc.
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
//Return an `MKPolylineRenderer` for the `MKPolyline` in the `MKMapViewDelegate`s method
if let polyline = overlay as? MKPolyline {
let testlineRenderer = MKPolylineRenderer(polyline: polyline)
testlineRenderer.strokeColor = .blue
testlineRenderer.lineWidth = 3.0
return testlineRenderer
}
fatalError("Something wrong...")
return MKOverlayRenderer()
}
I assume you have a button set up, and linked with IBAction.
You need to call the drawpolyline function from the button action giving the new coordinates in the same format as you have previously when calling it on launch but for your new location.
If you don't understand, maybe provide some more information on how you call the drawpolyline function on launch, I assume in the viewDidLoad.
If you don't know how to call a button use
#IBAction func showListPressed(_ sender: UIButton) {
// call drawpolyline function here with new coordinates
}
I am having trouble setting a pin color for my map annotation. I have a function in my MapView viewcontroller that pulls from an array from another view controller and depending on the case of the type, I want different pin colors for the map view. I am not sure how I can add the pin color information to the annotation within this switch statement. My understanding of annotations is rather weak so any explanations, rather than the solution itself, are greatly appreciated.
class ColorPointAnnotation: MKPointAnnotation {
var pinColor: UIColor
init(pinColor: UIColor) {
self.pinColor = pinColor
super.init()
}
}
func add(newLocation location_one:[String:Any]) {
let momentaryLat = (location_one["latitude"] as! NSString).doubleValue
let momentaryLong = (location_one["longitude"] as! NSString).doubleValue
var annotation = MKPointAnnotation()
switch location_one["type"] {
case "Tomorrow":
print("The pin color is red")
annotation = ColorPointAnnotation(pinColor: UIColor.red)
case "Next Week":
print("The pin color is green")
annotation = ColorPointAnnotation(pinColor: UIColor.green)
default:
print("The pin color is purple")
annotation = ColorPointAnnotation(pinColor: UIColor.purpleColor)
}
annotation.title = location_one["title"] as? String
annotation.coordinate = CLLocationCoordinate2D(latitude: momentaryLat as CLLocationDegrees, longitude: momentaryLong as CLLocationDegrees)
DispatchQueue.main.async {
self.map.addAnnotation(annotation)
}
self.map.centerCoordinate = annotation.coordinate
}
func mapView(_ map: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
// if (annotation is MKUserLocation) {
// return nil
// }
let identifier = "pinAnnotation"
var annotationView = map.dequeueReusableAnnotationView(withIdentifier: identifier) as? MKPinAnnotationView
if annotationView == nil {
annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
annotationView?.canShowCallout = true
let colorPointAnnotation = annotation as! ColorPointAnnotation
annotationView?.pinTintColor = colorPointAnnotation.pinColor
}
// else {
// annotationView?.annotation = annotation
//
// }
// map.showAnnotations(map.annotations, animated: true)
return annotationView
}
You need move your switch statement into the viewForAnnotation delegate method. Here you when you return a pin, you can customize the color and then return it.
Like so :
annotation.pinColor = MKPinAnnotationColorGreen;
Updated Answer :
You can subclass MKPointAnnotation, and add a property that stores the type of annotation.
When you create an annotation in your add method, set the property to what ever type of pin it is.
Now in the viewForAnnotation method, mapkit will give the annotation of your type. Look at the set property and determine which color pin to return.
Let me know if you want see some code.
My app currently has a local search which adds annotations for the search results. I want to set it up where when you select the annotation and click the call out button, it will open in the Maps application with directions to the annotation from the current device location. I'm having a few problems with this.
First of all, my call out button on the annotation does not appear. Secondly, I don't think I am detecting the selected annotation correctly.
Here is my code for the search and selected annotation:
func performSearch() {
matchingItems.removeAll()
let request = MKLocalSearchRequest()
request.naturalLanguageQuery = searchText.text
request.region = mapView.region
let search = MKLocalSearch(request: request)
search.startWithCompletionHandler({(response:
MKLocalSearchResponse!,
error: NSError!) in
if error != nil {
println("Error occured in search: \(error.localizedDescription)")
} else if response.mapItems.count == 0 {
println("No matches found")
} else {
println("Matches found")
for item in response.mapItems as! [MKMapItem] {
println("Name = \(item.name)")
println("Phone = \(item.phoneNumber)")
self.matchingItems.append(item as MKMapItem)
println("Matching items = \(self.matchingItems.count)")
var annotation = MKPointAnnotation()
var coordinates = annotation.coordinate
annotation.coordinate = item.placemark.coordinate
annotation.title = item.name
self.mapView.addAnnotation(annotation)
}
}
})
}
func mapView(mapView: MKMapView!, annotationView view: MKAnnotationView!,
calloutAccessoryControlTapped control: UIControl!) {
if self.mapView.selectedAnnotations?.count > 0 {
if let selectedLoc = self.mapView.selectedAnnotations[0] as? MKAnnotation {
println("Annotation has been selected")
let currentLoc = MKMapItem.mapItemForCurrentLocation()
let mapItems = NSArray(objects: selectedLoc, currentLoc)
let launchOptions = [MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving]
MKMapItem.openMapsWithItems([selectedLoc, currentLoc], launchOptions: launchOptions)
}
}
}
Any help will be appreciated, thanks in advance.
For the first issue:
A callout button needs to be set explicitly in the viewForAnnotation delegate method (default red pins don't have one). Here's a simple example of one possible implementation:
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
if annotation is MKUserLocation {
return nil
}
let reuseId = "pin"
var pinView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId) as? MKPinAnnotationView
if pinView == nil {
pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
pinView!.canShowCallout = true
pinView!.pinColor = .Purple
//next line sets a button for the right side of the callout...
pinView!.rightCalloutAccessoryView = UIButton.buttonWithType(.DetailDisclosure) as! UIButton
}
else {
pinView!.annotation = annotation
}
return pinView
}
For the second issue:
First, in calloutAccessoryControlTapped, the annotation is directly accessible using view.annotation so using the selectedAnnotations array in there is unnecessary.
Next, openMapsWithItems expects an array of MKMapItem objects but in the array you are passing ([selectedLoc, currentLoc]), selectedLoc is not an MKMapItem -- it is just some object that implements MKAnnotation.
Running this code will result in a crash with this error:
-[MKPointAnnotation dictionaryRepresentation]: unrecognized selector sent to instance
when the Maps app tries to use selectedLoc as if it was an MKMapItem.
Instead, you need to create an MKMapItem from the selectedLoc annotation. This can be done by first creating an MKPlacemark from the annotation using MKPlacemark(coordinate:addressDictionary:) and then creating an MKMapItem from the placemark using MKMapItem(placemark:).
Example:
func mapView(mapView: MKMapView!, annotationView view: MKAnnotationView!,
calloutAccessoryControlTapped control: UIControl!) {
let selectedLoc = view.annotation
println("Annotation '\(selectedLoc.title!)' has been selected")
let currentLocMapItem = MKMapItem.mapItemForCurrentLocation()
let selectedPlacemark = MKPlacemark(coordinate: selectedLoc.coordinate, addressDictionary: nil)
let selectedMapItem = MKMapItem(placemark: selectedPlacemark)
let mapItems = [selectedMapItem, currentLocMapItem]
let launchOptions = [MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving]
MKMapItem.openMapsWithItems(mapItems, launchOptions:launchOptions)
}