The default implementation of MKPointAnnotation shows titles beneath the pins, if desired:
I implemented viewFor Annotation in order to use a custom image, this eliminates MKPointAnnotation's title feature.
This code lives inside the networking code in the viewDidLoad in my MapViewController and pulls the location data from the shared instance array of location objects to create corresponding instances of my CustomAnnotation class:
// Create the annotations
var tempArray = [CustomAnnotation]()
for dictionary in Location.sharedInstance {
let lat = CLLocationDegrees(dictionary.latitude)
let long = CLLocationDegrees(dictionary.longitude)
let coordinates = CLLocationCoordinate2D(latitude: lat, longitude: long)
let name = dictionary.name
let annotation = CustomAnnotation(coordinates: coordinates, title: name)
tempArray.append(annotation)
}
// Add the annotations to the annotations array
self.mapView.removeAnnotations(self.annotationArray)
self.annotationArray = tempArray
self.mapView.addAnnotations(self.annotationArray)
}
This is my CustomAnnotation class:
class CustomAnnotation: NSObject, MKAnnotation {
var coordinate: CLLocationCoordinate2D = CLLocationCoordinate2D()
var title: String?
init(coordinates location: CLLocationCoordinate2D, title: String) {
super.init()
self.coordinate = location
self.title = title
}
}
And this is my viewFor implementation:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
// Don't want to show a custom image if the annotation is the user's location.
guard !(annotation is MKUserLocation) else {
return nil
}
let annotationIdentifier = "AnnotationIdentifier"
var annotationView: MKAnnotationView?
if let dequeuedAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: annotationIdentifier) {
annotationView = dequeuedAnnotationView
annotationView?.annotation = annotation
}
else {
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: annotationIdentifier)
}
if let annotationView = annotationView {
for location in Location.sharedInstance {
if annotationView.annotation?.coordinate.latitude == location.latitude && annotationView.annotation?.coordinate.longitude == location.longitude {
if location.rating < 2 {
annotationView.image = UIImage(named: "1star")
} else if location.rating == 2 {
annotationView.image = UIImage(named: "2star")
} else if location.rating == 2.5 {
annotationView.image = UIImage(named: "2star")
} else if location.rating == 3.0 {
annotationView.image = UIImage(named: "3star")
} else if location.rating == 3.5 {
annotationView.image = UIImage(named: "3star")
} else if location.rating == 4.0 {
annotationView.image = UIImage(named: "4star")
} else if location.rating == 4.5 {
annotationView.image = UIImage(named: "4star")
} else if location.rating > 4.5 {
annotationView.image = UIImage(named: "5star")
}
}
}
}
return annotationView
}
Is there a simple way of porting MKPointAnnotation's title format or do I have to make a custom view? Perhaps as importantly, is it safe to assume I would need to write some logic to prevent titles from closely located pins from being a jumble of text? Apple's MKPointAnnotation seems to have this logic built in, in that it just declines to show many titles.
Related
I am working in map view annotation.
The marker annotation should be displayed using the parking rule type
If paid pin image be "paid" and if free pin image be "free"
I am getting all annotation as "paid" image
I have attached my code below can any one help me in this issue to fix
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
// Don't want to show a custom image if the annotation is the user's location.
guard !(annotation is MKUserLocation) else {
return nil
}
// Better to make this class property
let annotationIdentifier = "AnnotationIdentifier"
var annotationView: MKAnnotationView?
if let dequeuedAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: annotationIdentifier) {
annotationView = dequeuedAnnotationView
annotationView?.annotation = annotation
}
else {
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: annotationIdentifier)
annotationView?.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
}
if let annotationView = annotationView {
// Configure your annotation view here
if parkingTypeArray.count > 0 {
for cameraa in parkingTypeArray.enumerated() {
if cameraa.element == "Free street parking" {
let pinImage = UIImage(named: "free")
annotationView.image = pinImage
}else if cameraa.element == "Paid street parking" {
let pinImage = UIImage(named: "paid")
annotationView.image = pinImage
}else if cameraa.element == "Paid parking" {
let pinImage = UIImage(named: "paid")
annotationView.image = pinImage
}
}
}
}
return annotationView
}
Same thing I Have Done with Custom MKPointAnnotation Class
class MyPointAnnotation : MKPointAnnotation {
var obj : ComparableData?
init(data_obj : ComparableData) {
self.obj = data_obj
super.init()
}
}
Setup Map
for item in self.Arr_Map_Comparables{
if item.Latitude != "" && item.Longitude != ""{
let annotation = MyPointAnnotation(data_obj: item)
annotation.coordinate = CLLocationCoordinate2D(latitude: Double(item.Latitude!)!, longitude: Double(item.Longitude!)!)
annotation.title = item.Full_Address
mapView.addAnnotation(annotation)
}
}
self.focusMarkers(markers: mapView.annotations, width: 50)
MapView Delegate Methods
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView?{
// Don't want to show a custom image if the annotation is the user's location.
guard !(annotation is MKUserLocation) else {
return nil
}
// Better to make this class property
let annotationIdentifier = "AnnotationIdentifier"
var annotationView: MKAnnotationView?
if let dequeuedAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: annotationIdentifier) {
annotationView = dequeuedAnnotationView
annotationView?.annotation = annotation
}
else {
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: annotationIdentifier)
}
if let annotationView = annotationView {
// Configure your annotation view here
annotationView.canShowCallout = true
if let annotation = annotationView.annotation as? MyPointAnnotation{
if annotation.obj?.Status_Mls == "Active"{
annotationView.image = UIImage(named: "active")
}else if annotation.obj?.Status_Mls == "Sold"{
annotationView.image = UIImage(named: "sold")
}else{
annotationView.image = UIImage(named: "other")
}
}
}
return annotationView
}
This what I'm trying to do:
Use a single custom annotation for all the locations that will populate the map. I have the custom image saved in the assets but, can't make it work.
My code is as follows:
ViewDidLoad()
if let locationDict = snap.value as? Dictionary<String, AnyObject> {
let lat = locationDict["LATITUDE"] as! CLLocationDegrees
let long = locationDict["LONGITUDE"] as! CLLocationDegrees
let title = locationDict["NAME"] as! String
let center = CLLocationCoordinate2D(latitude: lat, longitude: long)
_ = MKCoordinateRegion(center: center, span: MKCoordinateSpan(latitudeDelta: 0.10, longitudeDelta: 0.10))
let annotation = MKPointAnnotation()
annotation.coordinate = CLLocationCoordinate2DMake(lat, long)
annotation.title = title.capitalized // if you need title, add this line
self.mapView.addAnnotation(annotation)
}
Use a single custom annotation for all the locations that will populate the map. I have the custom image saved in the assets but, can't make it work (Details in the viewDidLoad)
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
var annotationView: MKAnnotationView?
if annotation.isKind(of: MKUserLocation.self) {
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: "User")
annotationView?.image = UIImage(named: "icon")
}
return annotationView
}
You should implement delegate method:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation.isKind(of: MKUserLocation.self) {
let annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: "User")
annotationView.image = UIImage(named: "icon")
return annotationView
}
let reuseId = "Image"
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseId)
if annotationView == nil {
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
annotationView?.canShowCallout = true
annotationView?.image = UIImage(named: "<<image name>>")
}
else {
annotationView?.annotation = annotation
}
return annotationView
}
You should implement delegate method like this.
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation {
return nil
}
var pinView : MKAnnotationView? = nil
let identifer = "pin"
pinView = mapView .dequeueReusableAnnotationView(withIdentifier: identifer)
if pinView == nil {
pinView = MKAnnotationView.init(annotation: annotation, reuseIdentifier: identifer)
}
pinView?.canShowCallout = true
pinView?.calloutOffset = CGPoint(x: -5, y: 5)
pinView?.rightCalloutAccessoryView = UIButton.init(type: .detailDisclosure) as UIView
let type = (annotation as! CustomAnnotation).type
if type == -1 {
pinView?.image = UIImage(named: "Img_gray_pin")?.resized(toWidth: 20)
} else if type == 0 {
pinView?.image = UIImage(named: "Img_gray_pin")?.resized(toWidth: 20)
} else if type == 1{
pinView?.image = UIImage(named: "Img_blue_pin")?.resized(toWidth: 20)
} else if type == 2 {
pinView?.image = UIImage(named: "Img_orange_pin")?.resized(toWidth: 25)
} else if type == 3 {
pinView?.image = UIImage(named: "Img_red_pin")?.resized(toWidth: 30)
} else {
pinView?.image = UIImage(named: "Img_gray_pin")?.resized(toWidth: 20)
}
return pinView
}
And then create CustomAnnotation Class
import MapKit
import UIKit
class CustomAnnotation: NSObject, MKAnnotation {
let title: String?
let subtitle: String?
let coordinate: CLLocationCoordinate2D
let tag : Int
let type : Int
init(title: String, address: String, coordinate: CLLocationCoordinate2D, tag : Int, type : Int) {
self.title = title
self.subtitle = address
self.coordinate = coordinate
self.tag = tag
self.type = type
super.init()
}
}
I am trying to make a lot different type of annotation. All annotation need to customize for the beautiful reason.
I know that it need to use viewFor Annotation, but how can I know what kind of the annotation?
func addZoneAnnotation() {
let zoneLocations = ZoneData.fetchZoneLocation(inManageobjectcontext: managedObjectContext!)
for zoneLocation in zoneLocations! {
let zoneCoordinate: CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: Double(zoneLocation["latitude"]!)!, longitude: Double(zoneLocation["longitude"]!)!)
let zoneAnnotation = MKPointAnnotation()
zoneAnnotation.coordinate = zoneCoordinate
map.addAnnotation(zoneAnnotation)
}
}
Subclass MKPointAnnotation to add whatever property that you want:
class MyPointAnnotation : MKPointAnnotation {
var identifier: String?
}
Then you can use it as follow:
func addZoneAnnotation() {
let zoneLocations = ZoneData.fetchZoneLocation(inManageobjectcontext: managedObjectContext!)
for zoneLocation in zoneLocations! {
let zoneCoordinate: CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: Double(zoneLocation["latitude"]!)!, longitude: Double(zoneLocation["longitude"]!)!)
let zoneAnnotation = MyPointAnnotation()
zoneAnnotation.coordinate = zoneCoordinate
zoneAnnotation.identifier = "an identifier"
map.addAnnotation(zoneAnnotation)
}
}
And finally when you need to access it:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
guard let annotation = annotation as? MyPointAnnotation else {
return nil
}
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "reuseIdentifier")
if annotationView == nil {
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: "reuseIdentifier")
} else {
annotationView?.annotation = annotation
}
// Now you can identify your point annotation
if annotation.identifier == "an identifier" {
// do something
}
return annotationView
}
I am trying to display custom pins on an MKMapView. These pins will have a custom image as well as a UILabel that will display a value.
I was able to successfully create the custom pin, with label. Right now the label displays a static value. I queried the data from a backend service like parse, and saved the data for each point. That way when the user taps on a certain point i can display the data in the viewController, however I am not sure how to pass this data from my query method into the didSelectAnnotation and viewForAnnotation methods.
I also would like to change the static value that the label shows to one queried from the server. I tried to do this by creating a class called CustomPointAnnotation, which inherits from MKPointAnnotation and has an initializer with three properties. these properties are set during the query, so how can I access these properties in the mapViewDidSelectAnnotationView, and the viewForAnnotation functions so that I can use the data for my needs. (for things like setting the text for a label within the viewController to a property of that specific annotation).
Below is an image that shows the viewController and what I have so far:
Here is the custom point class:
class CustomPointAnnotation: MKPointAnnotation {
var price: String!
var streetName: String!
var ratingValue: Int!
init?(price: String, streetName: String, ratingValue: Int) {
self.price = price
self.streetName = streetName
self.ratingValue = ratingValue
super.init()
}
}
Below is the query that I run in viewDidLoad:
func displayPoints() {
let pointsQuery = PFQuery(className: "testLocation")
let currentLocation = PFGeoPoint(location: locationManager.location)
pointsQuery.whereKey("location", nearGeoPoint: currentLocation, withinMiles: 2)
pointsQuery.findObjectsInBackgroundWithBlock { (points, error) -> Void in
if error == nil {
print("number of spots: \(points?.count)")
let spots = points! as [PFObject]
for pinPoint in spots {
let point = pinPoint["location"] as! PFGeoPoint
let price = String(pinPoint["price"])
let ratingValue = pinPoint["rating"] as! Int
let streetName = "Park Street, San Francisco CA"
self.customAnnotation = CustomPointAnnotation(price: price, streetName: streetName, ratingValue: ratingValue)
//// PRINT DATA OBTAINED FOR TESTING PURPOSES///////////////////////////////////////////////////////////
print(self.customAnnotation.price)
print(self.customAnnotation.streetName)
print(self.customAnnotation.ratingValue)
///////////////////////////////////////////////////////////////////////////////////////////////////////
self.customAnnotation!.coordinate = CLLocationCoordinate2DMake(point.latitude, point.longitude)
self.priceArray.append(pinPoint["price"])
self.customAnnotation!.price = pinPoint["price"] as? String
self.mapView.addAnnotation(self.customAnnotation!)
}
} else {
JSSAlertView().danger(self, title: "something went wrong", text: "error: \(error)")
}
}
}
here is the didSelectAnnotationView:
func mapView(mapView: MKMapView, didSelectAnnotationView view: MKAnnotationView) {
//var anot: MKAnnotation
if ((view.annotation?.isKindOfClass(MKUserLocation)) != nil){
view.image = nil
}
for anot in mapView.annotations {
print(mapView.annotations.count)
let annotationView = mapView.viewForAnnotation(anot)
if (annotationView != nil) {
annotationView?.image = UIImage(named: "pin")
priceLabel.textColor = UIColor.whiteColor()
}
//priceLabel.textColor = UIColor.blueColor()
view.image = UIImage(named: "pinselected")
print("image changed")
}
}
and finally the viewForAnnotation method:
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
if annotation.isKindOfClass(MKUserLocation){
return nil
}
if !(annotation is CustomPointAnnotation) {
print("all custom images added")
return nil
}
let reuseID = "identifier"
var annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseID)
if annotationView == nil {
annotationView = CustomAnnotationView(annotation: annotation, reuseIdentifier: reuseID, price: "13" )
annotationView?.canShowCallout = false
} else {
annotationView?.annotation = annotation
}
//let cpa = annotation as! CustomPointAnnotation
//let annotationView = CustomAnnotationView(annotation: annotation, reuseIdentifier: nil, price: "11")
//annotationView!.addSubview(priceLabel)
annotationView?.annotation = annotation
annotationView?.image = UIImage(named: "pin.png")
return annotationView
}
You can down cast in swift with the as operator. In didSelectAnnotationView the annotationView has an annotation property. Your custom annotation view will have your custom annotation as its annotation property, so you can attempt to down cast it to your subclass by saying:
if let annotation = view.annotation as? CustomPointAnnotation
Assuming that's possible, you will then have access to your subclass's properties.
func mapView(mapView: MKMapView, didSelectAnnotationView view: MKAnnotationView) {
//var anot: MKAnnotation
if ((view.annotation?.isKindOfClass(MKUserLocation)) != nil){
view.image = nil
}
for anot in mapView.annotations {
print(mapView.annotations.count)
let annotationView = mapView.viewForAnnotation(anot)
if (annotationView != nil) {
annotationView?.image = UIImage(named: "pin")
priceLabel.textColor = UIColor.whiteColor()
}
//priceLabel.textColor = UIColor.blueColor()
}
view.image = UIImage(named: "pinselected")
if let annotation = view.annotation as? CustomPointAnnotation
{
self.priceLabel.text = annotation.price //for example
//update the rest of your UI
}
print("image changed")
}
Similarly in viewForAnnotation you can down cast the MKAnnotation to CustomPointAnnotation and MKAnnotationView to CustomAnnotationView.
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
if annotation.isKindOfClass(MKUserLocation){
return nil
}
if !(annotation is CustomPointAnnotation) {
print("all custom images added")
return nil
}
let reuseID = "identifier"
let cpa = annotation as! CustomPointAnnotation
var annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseID) as! CustomAnnotationView
if annotationView == nil {
annotationView = CustomAnnotationView(annotation: cpa, reuseIdentifier: reuseID, price: cpa.price)
annotationView?.canShowCallout = false
} else {
annotationView?.annotation = cpa
annotationView?.price = cpa.price
}
annotationView?.image = UIImage(named: "pin.png")
return annotationView
}
Your CustomAnnotationView should update its price label when its price is set by implementing price's didSet.
I try to create a custom "badget" for my MKPointAnnotation in swift, but it fails as MKPointAnnotation does not have any property like image
var information = MKPointAnnotation()
information.coordinate = location
information.title = "Test Title!"
information.subtitle = "Subtitle"
information.image = UIImage(named: "dot.png") //this is the line whats wrong
Map.addAnnotation(information)
Anyone figured out a swift like solution for that?
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
if !(annotation is MKPointAnnotation) {
return nil
}
var annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier("demo")
if annotationView == nil {
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: "demo")
annotationView!.canShowCallout = true
}
else {
annotationView!.annotation = annotation
}
annotationView!.image = UIImage(named: "image")
return annotationView
}