Value of type 'MKAnnotation' has no member 'tipo' - mkannotation

I need annotation with different color by value from Firebase so I have create a class:
class AnnotationClass : MKPointAnnotation {
var parametro: String?
var titolo:String?
var sottotitolo: String?
var tipo: String?
}
then set it:
let annotation = AnnotationClass()
annotation.titolo = location.citta?.uppercased() as? String
annotation.sottotitolo = "\(location.titolo!) POSTI"
annotation.parametro = "\(location.id!)"
annotation.tipo = "\(location.tipo!)"
annotation.title = "\(location.tipo!)"
the prime is here how can get it in the next function? if annotationView?.annotation?.tipo == "CONCORSO"
ERROR: Value of type 'MKAnnotation' has no member 'tipo'
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
var annotationView: MKMarkerAnnotationView? = mapView.dequeueReusableAnnotationView(withIdentifier: "mia2") as? MKMarkerAnnotationView
annotationView = MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: "mia2")
if annotationView?.annotation?.tipo == "CONCORSO" {
annotationView?.markerTintColor = #colorLiteral(red: 0.2392156869, green: 0.6745098233, blue: 0.9686274529, alpha: 1)
annotationView?.glyphText = "C"
} else {
annotationView?.markerTintColor = #colorLiteral(red: 0.9254901961, green: 0.2352941176, blue: 0.1019607843, alpha: 1)
annotationView?.glyphText = "A"
}
return annotationView
}

You need to cast annotation: MKAnnotation to your custom class:
let myCustomAnnotation = annotation as? AnnotationClass
Below I have fixed multiple issues within your delegate method
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
var annotationView: MKMarkerAnnotationView! = mapView.dequeueReusableAnnotationView(withIdentifier: "mia2") as? MKMarkerAnnotationView
if (annotationView == nil) {
// Create a new annotation view
annotationView = MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: "mia2")
} else {
// update existing (reusable) annotationView's annotation
annotationView.annotation = annotation
}
if let myCustomAnnotation = annotation as? AnnotationClass, myCustomAnnotation.tipo == "CONCORSO" {
annotationView.markerTintColor = #colorLiteral(red: 0.2392156869, green: 0.6745098233, blue: 0.9686274529, alpha: 1)
annotationView.glyphText = "C"
} else {
annotationView.markerTintColor = #colorLiteral(red: 0.9254901961, green: 0.2352941176, blue: 0.1019607843, alpha: 1)
annotationView.glyphText = "A"
}
return annotationView
}
You can also shorten the if-statement to something like:
if (annotation as? AnnotationClass)?.tipo == "CONCORSO" {
...
}

Related

mapkit - displaying custom images

Below is my code displaying annotations, I have a question why custom images are displayed only the second time the view is loaded?
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
guard annotation is MKPointAnnotation else { return nil }
let identifier = "Annotation"
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier)
if annotationView == nil {
annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
annotationView!.isEnabled = true
annotationView!.canShowCallout = true
print("1")
} else {
annotationView!.annotation = annotation
print("2")
}
let detailAnnotation = annotation as! WaypointsAnnotation
if (detailAnnotation.type == "Current") {
annotationView!.image = UIImage(named: "point5")
//let transform = CGAffineTransform(scaleX: 0.1, y: 0.1)
//annotationView!.transform = transform
}
if (detailAnnotation.type == "Waypoint") {
annotationView!.image = UIImage(named: "point6")
//let transform = CGAffineTransform(scaleX: 0.1, y: 0.1)
//annotationView!.transform = transform
}
Thanks for reply.
Try changing your annotation type from MKPinAnnotationView to MKAnnotationView.

How to make a custom MKAnnotationView with XIB

I want to have a custom MKAnnotationView. I've created a xib file in IB and set its class to MyAnnotationView.
class MyAnnotationView: MKAnnotationView {
override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
#IBOutlet weak var textLabel: UILabel!
#IBOutlet weak var busIcon: UIImageView!
}
Here's how the xib looks like - it has a textLabel and a busIcon linked:
I'm using the viewFor annotation delegate method to create views for all annotations:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
// Don't want to show a custom image if the annotation is the user's location.
if (annotation is MKUserLocation) {
return nil
} else {
let annotationIdentifier = "AnnotationIdentifier"
var annotationView: MyAnnotationView?
if let dequeuedAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "AnnotationIdentifier") as? MyAnnotationView {
annotationView = dequeuedAnnotationView
annotationView?.annotation = annotation
} else {
// if no views to dequeue, create an Annotation View
let av = MyAnnotationView(annotation: annotation, reuseIdentifier: annotationIdentifier)
av.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
annotationView = av
}
if let annotationView = annotationView {
annotationView.canShowCallout = true // callout bubble
annotationView.image = UIImage(named: "Delivery")
annotationView.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
}
return annotationView
}
}
The annotationView.image = UIImage(named: "Delivery")
&
AnnotationView.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
are there just to check if the code is working and display a sample view on the map, as they use the standard properties inherited from MKAnnotationView.
I don't know how to make the viewFor annotation method use the XIB I have created. Could anyone please help me with that? I searched for the solution, but only found something relevant in Obj C.
Thank you!
1- Create a view subclass of UIView with xib say CallView
2- Inside viewforAnnotation
let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "id")
let customView = Bundle.main.loadNibNamed("CallView", owner: self, options: nil).first! as! CallView
// here configure label and imageView
annotationView.addSubview(customView)
UPDATED CODE BASED ON Sh-Khan's answer
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
// Don't want to show a custom image if the annotation is the user's location.
if (annotation is MKUserLocation) {
return nil
} else {
let annotationIdentifier = "AnnotationIdentifier"
let nibName = "MyAnnotationView"
let viewFromNib = Bundle.main.loadNibNamed(nibName, owner: self, options: nil)?.first as! MyAnnotationView
var annotationView: MyAnnotationView?
// if there is a view to be dequeued, use it for the annotation
if let dequeuedAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: annotationIdentifier) as? MyAnnotationView {
if dequeuedAnnotationView.subviews.isEmpty {
dequeuedAnnotationView.addSubview(viewFromNib)
}
annotationView = dequeuedAnnotationView
annotationView?.annotation = annotation
} else {
// if no views to dequeue, create an Annotation View
let av = MyAnnotationView(annotation: annotation, reuseIdentifier: annotationIdentifier)
av.addSubview(viewFromNib)
annotationView = av // extend scope to be able to return at the end of the func
}
// after we manage to create or dequeue the av, configure it
if let annotationView = annotationView {
annotationView.canShowCallout = true // callout bubble
annotationView.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
annotationView.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
let customView = annotationView.subviews.first as! MyAnnotationView
customView.frame = annotationView.frame
customView.textLabel.text = (annotationView.annotation?.title)!
}
return annotationView
}
}
**Create Custom MKPointAnnotation Class**
import UIKit
import MapKit
class CustomPointAnnotation: MKPointAnnotation {
var id : Int
var url : String
init(id : Int , url : String ) {
self.id = id
self.url = url
}
}
Create Xib for MarkerView Class for Annotation View
class MarkerView: MKAnnotationView {
#IBOutlet weak var imgVwUser: UIImageView!
init(annotation: CustomPointAnnotation?, reuseIdentifier: String?) {
super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let hitView = super.hitTest(point, with: event)
if (hitView != nil)
{
self.superview?.bringSubviewToFront(self)
}
return hitView
}
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
let rect = self.bounds
var isInside: Bool = rect.contains(point)
if(!isInside)
{
for view in self.subviews
{
isInside = view.frame.contains(point)
if isInside
{
break
}
}
}
return isInside
}
}
Add MKMapView Delegates In Your ViewController
extension YourViewController : MKMapViewDelegate {
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
// Don't want to show a custom image if the annotation is the user's location.
if (annotation is MKUserLocation) {
return nil
} else {
let annotationIdentifier = "AnnotationIdentifier"
let nibName = "MarkerView"
let viewFromNib = Bundle.main.loadNibNamed(nibName, owner: self, options: nil)?.first as! MarkerView
var annotationView: MarkerView?
// if there is a view to be dequeued, use it for the annotation
if let dequeuedAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: annotationIdentifier) as? MarkerView {
if dequeuedAnnotationView.subviews.isEmpty {
dequeuedAnnotationView.addSubview(viewFromNib)
}
annotationView = dequeuedAnnotationView
annotationView?.annotation = annotation
} else {
// if no views to dequeue, create an Annotation View
let av = MarkerView(annotation: annotation as? CustomPointAnnotation, reuseIdentifier: annotationIdentifier)
av.addSubview(viewFromNib)
annotationView = av // extend scope to be able to return at the end of the func
}
// after we manage to create or dequeue the av, configure it
if let annotationView = annotationView {
annotationView.canShowCallout = false // callout bubble
if let annotation = annotation as? CustomPointAnnotation {
annotationView.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
annotationView.frame = CGRect(x: 0, y: 0, width: 66, height: 75)
let customView = annotationView.subviews.first as? MarkerView
customView?.frame = annotationView.frame
let image = annotation.url
let imageUrl = URL(string: image)
customView?.imgVwUser.sd_setImage(with: imageUrl, placeholderImage: UIImage(named:"defaults"), options: [.refreshCached], completed: nil)
}
}
return annotationView
}
}
}
ADD Annotation to the mapview
extension YourViewController {
func addAnnotation(){
let annotationsToRemove = mapView.annotations.filter { $0 !== mapView.userLocation }
mapView.removeAnnotations( annotationsToRemove )
var annotations: [CustomPointAnnotation] = []
for i in 0..<self.arrayData.count {
let customPoints = CustomPointAnnotation.init(id: arrayData[i].id ?? 0, url: arrayData[i].url)
let location = CLLocationCoordinate2DMake(self.arrayData[i].lat ?? 0, self.arrayData[i].lng ?? 0)
customPoints.coordinate = location
annotations.append(customPoints)
}
mapView.addAnnotations(annotations)
}
}

ViewForAnnotation not Called in Swift 3

I want to open a new viewcontroller when i click the PinAnnotation. But ViewForannotation is not calling when i click, But It is showing a pop up message when i click on Pin. Don't know what is happening. Here is my Code:
import UIKit
import MapKit
class Pin: NSObject, MKAnnotation {
var coordinate: CLLocationCoordinate2D
var key: String
var title: String?
var age: String?
var category: String?
var color: MKPinAnnotationColor = MKPinAnnotationColor.purple
var subtitle: String?
init(key: String, name: String , age: String , category: String , color: MKPinAnnotationColor) {
self.coordinate = CLLocationCoordinate2D(latitude: 0, longitude: 0)
self.key = key
self.title = name
self.color = color
self.subtitle = age
self.category = category
//print("keyy: ", key)
//print("title: ", name)
}
}
This is my MapViewControllerClass:
import UIKit
import GeoFire
import MapKit
import Firebase
import CoreLocation
class MapViewController: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate, UIAlertViewDelegate {
//#IBOutlet weak var sidebarButton: UIBarButtonItem!
#IBOutlet weak var mapView: MKMapView!
//#IBOutlet weak var chatbarButton: UIBarButtonItem!
/* ---> NEW MAP-IT <--- */
let locationManager = CLLocationManager()
let imagePicker = UIImagePickerController()
var regionQuery: GFRegionQuery?
var foundQuery: GFCircleQuery?
var annotations: Dictionary<String, Pin> = Dictionary(minimumCapacity: 8)
var lastExchangeKeyFound: String?
var lastExchangeLocationFound: CLLocation?
var location: CLLocation!
// var location = CLLocation(latitude: 37.33233141, longitude: -122.0312186)
//let location = CLLocation(latitude: 37.33209999, longitude: -122.0326666)
var circle: MKCircle!
var index = 0
var val1Lat: Double!
var val2Lat: Double!
var val1Long: Double!
var val2Long: Double!
var flag = false
var userName = ""
var age = ""
var category = ""
var uid = ""
override func viewDidLoad() {
super.viewDidLoad()
uid = DataService.ds.currentUserID
self.mapView.delegate = self
// locationManager.requestAlwaysAuthorization()
if (CLLocationManager.authorizationStatus() == CLAuthorizationStatus.authorizedAlways) || (CLLocationManager.authorizationStatus() == CLAuthorizationStatus.authorizedWhenInUse) {
print("Authorized")
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
} else {
print("Application is not authorized to use location services")
//- TODO: Unauthorized, requests permissions again and makes recursive call
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if manager.location?.coordinate != nil {
if flag == true {
if (location.coordinate.latitude != manager.location?.coordinate.latitude) || (location.coordinate.longitude != manager.location?.coordinate.longitude) {
location = manager.location!//.coordinate
print("lat: \(location.coordinate.latitude)")
print("long: \(location.coordinate.longitude)")
animateMap(location: location)
let key = DataService.ds.currentUserID
geofire!.setLocation(location, forKey: key)
}
} else {
print("Else:LocationManag")
location = manager.location!
print("lat: \(location.coordinate.latitude)")
print("long: \(location.coordinate.longitude)")
animateMap(location: location)
flag = true
let key = DataService.ds.currentUserID
geofire!.setLocation(location, forKey: key)
}
}
}
override func viewDidAppear(_ animated: Bool) {
print("DidAppear")
self.mapView.userLocation.addObserver(self, forKeyPath: "location", options: NSKeyValueObservingOptions(), context: nil)
}
override func viewDidDisappear(_ animated: Bool) {
print("DidDisAppear")
locationManager.stopUpdatingLocation()
self.mapView.userLocation.removeObserver(self, forKeyPath: "location", context: nil)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
print("In observer")
if (self.mapView.showsUserLocation) && (self.mapView.userLocation.location) != nil
{
let span = MKCoordinateSpanMake(0.0125, 0.0125)
let region = MKCoordinateRegion(center: self.mapView.userLocation.location!.coordinate /*location.coordinate*/, span: span)
self.mapView.setRegion(region, animated: true)
if foundQuery == nil {
print("FoundQuery nill")
foundQuery = geofire?.query(at: /*location*/self.mapView.userLocation.location, withRadius: 0.2)
foundQuery!.observe(GFEventType.keyEntered, with: { (key: String?, location:CLLocation?) -> Void in
//print("here")
DataService.ds.userRef.child(key!).observeSingleEvent(of: .value, with: { (snapshot) in
if let userDictionary = snapshot.value as? Dictionary<String , AnyObject>
{
self.userName = userDictionary["firstName"] as! String
self.age = userDictionary["age"] as! String
self.category = userDictionary["category"] as! String
}
self.lastExchangeKeyFound = key
self.lastExchangeLocationFound = location
if key != DataService.ds.currentUserID {
let annotation = Pin(key: key!, name: self.userName , age: self.age , category: self.category , color: MKPinAnnotationColor.green)
annotation.coordinate = (location?.coordinate)!
annotation.title = self.userName + " - " + self.age
annotation.subtitle = self.category
// if self.category == "Trainer"{
// annotation.color = MKPinAnnotationColor.green
// }else{
// annotation.color = MKPinAnnotationColor.purple
// }
self.mapView.addAnnotation(annotation)
self.annotations[key!] = annotation
}
})
self.foundQuery?.observe(.keyExited, with: { (key, location) in
if key != DataService.ds.currentUserID {
if self.annotations[key!] != nil {
self.mapView.removeAnnotation(self.annotations[key!]!)
self.annotations[key!] = nil
}
}
})
})
}
else
{
foundQuery?.center = self.mapView.userLocation.location
}
}
}
//Click Event For Annotation:
// func mapView(mapView: MKMapView!, annotationView view: MKAnnotationView!,
// calloutAccessoryControlTapped control: UIControl!) {
//
// if control == view.rightCalloutAccessoryView {
// print("Disclosure Pressed! \(view.annotation?.subtitle)")
// }
//
// }
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
print("inPin1")
if annotation is Pin {
print("inPin2")
let pinAnnotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "myPin")
pinAnnotationView.pinColor = .purple
pinAnnotationView.isDraggable = true
pinAnnotationView.canShowCallout = true
pinAnnotationView.animatesDrop = true
let deleteButton = UIButton.init(type: UIButtonType.custom) as UIButton
// let deleteButton = UIButton.withType(UIButtonType.custom) as UIButton
deleteButton.frame.size.width = 44
deleteButton.frame.size.height = 44
deleteButton.backgroundColor = UIColor.red
deleteButton.setImage(UIImage(named: "xbutton"), for: .normal)
pinAnnotationView.leftCalloutAccessoryView = deleteButton
return pinAnnotationView
}
return nil
}
func mapView(mapView: MKMapView!, annotationView view: MKAnnotationView!, calloutAccessoryControlTapped control: UIControl!) {
if let annotation = view.annotation as? Pin {
mapView.removeAnnotation(annotation)
}
}
// func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView {
// var annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "loc")
// annotationView.canShowCallout = true
// annotationView.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
// return annotationView
// }
//
// func mapView(mapView: MKMapView, didSelectAnnotationView view: MKAnnotationView) {
// mapView.deselectAnnotation(view.annotation, animated: true)
// var controller = self.storyboard!.instantiateViewController(withIdentifier: "DetailsPopover")
// controller.annotation! = view.annotation
// self.popover = UIPopoverController(contentViewController: controller)
// self.popover.delegate = self
// self.popover.presentPopoverFromRect(view.frame, inView: view.superview!, permittedArrowDirections: .Any, animated: true)
// }
// func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
// print("helloooo")
// self.performSegue(withIdentifier: "detailView", sender: view)
// }
// func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
// // simple and inefficient example
// let annotationView = MKPinAnnotationView()
// if category == "Trainer"{
// annotationView.pinTintColor = .purple
// }else{
// annotationView.pinTintColor = .green
// }
//
//
//
// return annotationView
// }
func animateMap(location: CLLocation)
{
// print("animate Map")
let region = MKCoordinateRegionMakeWithDistance(location.coordinate, 1000, 1000)
mapView.setRegion(region, animated: true)
addRadiusCircle(location: location)
}
func addRadiusCircle(location: CLLocation)
{
//print("Add Radius")
//self.mapView.delegate = self
if circle != nil
{
self.mapView.remove(circle)
}
circle = MKCircle(center: location.coordinate, radius: 200 as CLLocationDistance)
self.mapView.add(circle)
}
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer
{
if overlay is MKCircle
{
let circle = MKCircleRenderer(overlay: overlay)
circle.strokeColor = UIColor.red
circle.fillColor = UIColor(red: 255, green: 0, blue: 0, alpha: 0.1)
circle.lineWidth = 1
return circle
}
else {
let temp = MKOverlayRenderer()
return temp
}
}
}
I am Using Geofire to get the nearby users in a particular radius and then pin that users. Please help.
You are saying:
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView!
That will certainly never be called. In Swift 3, the correct signature is:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView?
All you have to do is consult the documentation:
https://developer.apple.com/reference/mapkit/mkmapviewdelegate/1452045-mapview
In case anyone else was having the same issue while still using the correct signature, ensure you've set your mapView delegate:
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self
}

How to change default background color of callout bubble with detailCalloutAccessoryView

In my app I have the following sutuation.
I've implemented a custom callout bubble with custom detailCalloutAccessoryView with two labels inside.
I know how to change the color of detailCalloutAccessoryView with this line.
view.detailCalloutAccessoryView?.backgroundColor = UIColor.red
But I can't figure out how to change background color of the main bubble (it is transparent grey/white now). With view.detailCalloutAccessoryView?.backgroundColor = UIColor.red line my calloutbubble looks like this:
But I want my custom bubble to look like this:
Here is my viewFor annotation method:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation {
return nil
}
let identifier = "pin"
var view : MKAnnotationView
if let dequedView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier) {
dequedView.annotation = annotation
view = dequedView
} else {
view = MKAnnotationView(annotation: annotation, reuseIdentifier: identifier)
view.canShowCallout = true
}
let pinImage = UIImage.init(named: "customPin")
DispatchQueue.main.async(execute: {
view.detailCalloutAccessoryView?.backgroundColor = UIColor.red
})
view.image = pinImage
configureDetailView(annotationView: view)
return view
}
I'm working in Xcode 8 w/ Swift 3.
It would be also interesting to know how to change font type and default black color of the title from black to another color.
In detail view i can easily change color of my custom labels in xib file but don't know how to access default title properties.
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
//design your custom view and set it to detailCalloutAccessoryView
view.detailCalloutAccessoryView = yourDetailView
detail.superview?.superview?.backgroundColor = yourColor
// This view is type of MKSmallCalloutView
}
UIViewCallout is a private class. If you want custom callout view:
disable standart callout view.canShowCallout = false
implement MKMapViewDelegate methods with your custom UIView for callout:
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
let redCalloutView = RedCalloutView(view.annotation)
view.addSubview(redCalloutView)
}
func mapView(_ mapView: MKMapView, didDeselect view: MKAnnotationView) {
view.subviews.forEach {
if $0 is RedCalloutView {
$0.removeFromSuperview()
}
}
}
I had created the code for your requirement please find the below url for download the code and review it.
Link : https://www.dropbox.com/s/o2howwqceq8rsgu/MapInformation.zip?dl=0
Environment : Xcode 8 and Swift3
Highlight the code which I had done it.
I had taken the approach to display the Popup(UIPresentationController) instead of callout. For more information please find the below code.
A) I had used the UIButton to display as annotation on the MapView and display the popup when user click on it.
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation {
return nil
}
let identifier = "pin"
var annotationView = self.mapView.dequeueReusableAnnotationView(withIdentifier: identifier) as! AnnotationView?
if annotationView == nil {
annotationView = AnnotationView(annotation: annotation, reuseIdentifier: identifier)
annotationView?.canShowCallout = false
}
else {
annotationView?.annotation = annotation
}
//Take the UIButton and implement the touchupinside action for showing the popup.
let pinImage = UIImage.init(named: "customPin")
annotationView?.frame = CGRect(x: 0, y: 0, width: (pinImage?.size.width)!, height: (pinImage?.size.width)!)
annotationView?.mapPin = UIButton(frame: (annotationView?.frame)!);
annotationView?.mapPin.addTarget(self, action: #selector(ViewController.showPopup(sender:)), for: .touchUpInside)
annotationView?.addSubview((annotationView?.mapPin)!)
annotationView?.mapPin.setImage(pinImage, for: .normal)
return annotationView
}
B) Display the popup when user click on the annotation.
func showPopup(sender: UIButton!) {
let popupVC = self.storyboard?.instantiateViewController(withIdentifier: "Popup") as? Popup
popupVC?.preferredContentSize = CGSize(width: 250, height: 150)
popupVC?.modalPresentationStyle = UIModalPresentationStyle.popover
let rect = sender.superview?.convert(sender.frame, to: self.view)
popupVC?.popoverPresentationController?.delegate = self;
popupVC?.popoverPresentationController?.sourceView = self.view
popupVC?.popoverPresentationController?.sourceRect = rect!
popupVC?.popoverPresentationController?.backgroundColor = UIColor.red
self.present(popupVC!, animated: true, completion: nil)
}
Note
If you want to change the popup color from red to other different
color then you can do only single line of coding by changing the color name.
popupVC?.popoverPresentationController?.backgroundColor = UIColor.red
Please look into the below screenshot.
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
for v in view.subviews{
if v.subviews.count > 0{
v.subviews[0].backgroundColor = UIColor.red
}
}
}
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
for v in view.subviews{
if v.subviews.count > 0 {
let colloutView = v.subviews[0]
colloutView.backgroundColor = UIColor(red: 0.0/255.0, green: 0.0/255.0, blue: 0.0/255.0, alpha: 0.8)
if colloutView.subviews.count > 0 {
if colloutView.subviews[0].subviews.count > 0{
colloutView.subviews[0].subviews.forEach { (view) in
if let label = view as? UILabel{
label.textColor = UIColor.white
}
}
}
}
}
}
}

SWIFT 2: multiline MKPointAnnotation

Is there a way to create multiline annotations?
Here is my code:
1) My customAnnotation class
import UIKit
import MapKit
class CustomAnnotation: NSObject, MKAnnotation {
var title: String?
var subtitle: String?
var address: String = ""
var phoneNumber: String = ""
var workingHours: String = ""
var coordinate: CLLocationCoordinate2D
init( title: String,
subtitle: String,
address: String,
phoneNumber: String,
workingHours: String,
coordinate: CLLocationCoordinate2D ){
self.title = title
self.subtitle = subtitle
self.address = address
self.phoneNumber = phoneNumber
self.workingHours = workingHours
self.coordinate = coordinate
super.init()
}
}
2) my view controller with map:
class MapViewController: UIViewController, MKMapViewDelegate {
#IBOutlet weak var theMapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
let latitude: CLLocationDegrees = 54.685290
let longitude : CLLocationDegrees = 25.279661
let latDelta : CLLocationDegrees = 0.2
let lonDelta : CLLocationDegrees = 0.2
let theSpan : MKCoordinateSpan = MKCoordinateSpanMake(latDelta, lonDelta)
let branchLocation : CLLocationCoordinate2D = CLLocationCoordinate2DMake(latitude, longitude)
let theRegion : MKCoordinateRegion = MKCoordinateRegionMake(branchLocation, theSpan)
let annotation = CustomAnnotation(title: "Brach1", subtitle: "some text for B1", address: "address str 12-12", phoneNumber: "123 321 123", workingHours: "00 - 24", coordinate: branchLocation)
self.theMapView.setRegion(theRegion, animated: true)
self.theMapView.addAnnotation(annotation)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
let identifier = "MyPin"
if annotation.isKindOfClass(MKUserLocation) {
return nil
}
var annotationView: MKPinAnnotationView? = mapView.dequeueReusableAnnotationViewWithIdentifier(identifier) as? MKPinAnnotationView
if annotationView == nil {
annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
annotationView?.canShowCallout = true
let multiLineView = UIView(frame: CGRectMake(0, 0, 100, 40))
let label1 = UILabel(frame: CGRectMake(0,0,100,20))
label1.text = "Some text1"
multiLineView.addSubview(label1)
let label2 = UILabel(frame: CGRectMake(0,20,100,20))
label2.text = "some text2"
multiLineView.addSubview(label2)
annotationView!.rightCalloutAccessoryView = multiLineView
} else {
annotationView!.annotation = annotation
}
return annotationView
}
}
What I get:
If I would use
annotationView!.leftCalloutAccessoryView = multiLineView
I would get:
I haven't found any other methods. I would expect something like bottomCalouttAccessoryView to get something like:
That is very wired for me that the height of annotation is not amendable. (that is why my three lables don't even fit there)
I was trying to play with autoresize methods of annotationview but with no luck:(
The subtitle is a string that prints to a single line label, you would likely have to make a subclass in order to add multiple lines.
have you looked at:
How to display 2 lines of text for subtitle of MKAnnotation and change the image for the button on the right?
especially this part (I converted it to Swift for you)
var multiLineView= UIView(frame: CGRectMake(0, 0, 23, 23))
multiLineView.addSubview(lable1)
multiLineView.addSubview(lable2)
annotationView.leftCalloutAccessoryView = multiLineView
UPDATE
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView! {
var pinView = mapView.dequeueReusableAnnotationViewWithIdentifier("pin")
if pinView == nil {
pinView = MKAnnotationView(annotation: annotation, reuseIdentifier: "pin")
pinView!.canShowCallout = true
// create a new View
var multiLineView= UIView(frame: CGRectMake(0, 0, 23, 23))
// add all the labels you need here
let label1 = UILabel(frame: CGRectMake(0,10,10,10))
label1.text = "Some text"
multiLineView.addSubview(label1)
let label2 = UILabel(frame: CGRectMake(0,20,10,10))
label2.text = "some text"
multiLineView.addSubview(label2)
pinView!.leftCalloutAccessoryView = multiLineView
}
else {
pinView!.annotation = annotation
}
return pinView
}
plz use this
i programmatically set NSLayoutConstraint.
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
let identifier = "MyPin"
if annotation.isKindOfClass(MKUserLocation) {
return nil
}
var annotationView: MKPinAnnotationView? = mapView.dequeueReusableAnnotationViewWithIdentifier(identifier) as? MKPinAnnotationView
if annotationView == nil {
annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
annotationView?.canShowCallout = true
let label1 = UILabel(frame: CGRectMake(0, 0, 200, 21))
label1.text = "Some text1 some text2 some text2 some text2 some text2 some text2 some text2"
label1.numberOfLines = 0
annotationView!.detailCalloutAccessoryView = label1;
let width = NSLayoutConstraint(item: label1, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.LessThanOrEqual, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: 200)
label1.addConstraint(width)
let height = NSLayoutConstraint(item: label1, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: 90)
label1.addConstraint(height)
} else {
annotationView!.annotation = annotation
}
return annotationView
}
}
and output