SWIFT 2: multiline MKPointAnnotation - swift

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

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 pass firebase data information to another viewcontroller when mapview annotation is clicked?

I know similar questions have been asked many times before. Just can't find one solution that fits my need. Please help
I have been trying to load information from firebase onto mapview. I am able to append all the information view map annotationviews. however what I want to do is pass the firebase data to a new viewcontroller when the map annotationviews are clicked. I am able to segue into another viewcontroller when clicking the annotations, but the wrong firebase data is loaded
Code I have tried thus far:
override func viewDidLoad() {
super.viewDidLoad()
self.mapView.delegate = self
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.requestLocation()
observeGeofire()
}
func observeGeofire() {
let dataRef = Database.database().reference().child("Shoutout")
dataRef.observeSingleEvent(of: .value, with: { (snapshot) in
for snap in snapshot.children {
let postSnap = snap as! DataSnapshot
if let dict = postSnap.value as? [String: AnyObject] {
let Lat = dict["lat"] as! CLLocationDegrees
let Long = dict["long"] as! CLLocationDegrees
let EVENT = dict["Event Type"] as? String
let Radmap = self.Rad
var annotations = MKPointAnnotation()
annotations.coordinate = CLLocationCoordinate2D(latitude: Lat, longitude: Long)
annotations.title = dict["title"] as? String
let loc = CLLocation(latitude: Lat, longitude: Long)
let distanceFromUser = (self.Location.distance(from: loc))/1000
let span = MKCoordinateSpanMake(0.25, 0.25)
if distanceFromUser < Radmap && EVENT! == self.sovcCET {
self.mapView.addAnnotation(annotations)
print("testing text for \(self.sovcCET)")
} else if distanceFromUser < Radmap && self.sovcCET == "All Events"{
self.mapView.addAnnotation(annotations)
} else if distanceFromUser < Radmap && self.sovcCET == "Choose Event" {
self.mapView.addAnnotation(annotations)
} else {
print("failed test")
}
}
}
})
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView?{
if annotation is MKUserLocation {
//return nil so map view draws "blue dot" for standard user location
return nil
}
let reuseId = "pin"
var pinView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseId) as? MKPinAnnotationView
pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
pinView?.pinTintColor = UIColor.red
pinView?.canShowCallout = true
let smallSquare = CGSize(width: 30, height: 30)
let button = UIButton(frame: CGRect(origin: CGPoint(x: 0,y :0), size: smallSquare))
let rightButton = UIButton(type: .contactAdd)
rightButton.tag = annotation.hash
pinView?.animatesDrop = true
pinView?.canShowCallout = true
pinView?.rightCalloutAccessoryView = rightButton
pinView?.leftCalloutAccessoryView = button
return pinView
}
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView,
calloutAccessoryControlTapped control: UIControl) {
//mapToDetails
if control == view.rightCalloutAccessoryView {
performSegue(withIdentifier: "mapToDetails", sender: self)
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "mapToDetails" {
if let destination = segue.destination as? shoutoutDetailViewController {
destination.shoutoutSelected = ShoutoutSelected
}
}
}
ShoutoutSelected is actually an instantiation of a class Shoutout containing multiple variable as shown below:
class Shoutout: NSObject {
// CLLocationDegrees
var event: String?
var when: String?
var Place: String?
var details: String?
var timestamp: NSNumber?
var whenTimeStamp: NSNumber?
var picsImageUrl: String?
var fromId: String?
var lat: CLLocationDegrees?
var long: CLLocationDegrees?
var title: String?
var subtitle: String?
var shoutoutId: String?
var distance: Double?
var tit: String?
var capt: String?
init(dictionary: [String: Any]) {
self.event = dictionary["Event Type"] as? String
self.when = dictionary["Date and Time"] as? String
self.whenTimeStamp = dictionary["Date and Time"] as? NSNumber
self.Place = dictionary["place"] as? String
self.details = dictionary["details"] as? String
self.timestamp = dictionary["TIMESTAMP"] as? NSNumber
self.picsImageUrl = dictionary["postPicUrl"] as? String
self.fromId = dictionary["FROM"] as? String
self.lat = dictionary["lat"] as? CLLocationDegrees
self.long = dictionary["long"] as? CLLocationDegrees
self.title = dictionary["title"] as? String
self.subtitle = dictionary["subtitle"] as? String
self.shoutoutId = dictionary ["ResponseID"] as? String
self.tit = dictionary ["Event Title"] as? String
self.capt = dictionary ["Captions"] as? String
}
func shouterPartnerId() -> String? {
return fromId
}
func soid() -> String? {
return shoutoutId
}
}
Thanks again
you can use below code instead of prepare segue
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView,
calloutAccessoryControlTapped control: UIControl) {
let vc = self.storyboard?.instantiateViewController(withIdentifier:
"DestinationViewController") as! DestinationViewController
vc.shoutoutSelected = ShoutoutSelected
self.navigationController?.pushViewController(vc, animated: true)
}
When using performSegue, performSegue(withIdentifier: "mapToDetails", sender: self) you can send your object in sender . So instead of sending sender : self try sender: ShoutoutSelected
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView,
calloutAccessoryControlTapped control: UIControl) {
//mapToDetails
if control == view.rightCalloutAccessoryView {
performSegue(withIdentifier: "mapToDetails", sender: ShoutOutSelected)
}
}
Now in prepareForSegue, you can retrieve the value of sender and pass it to the destination controller.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "mapToDetails" {
if let destination = segue.destination as? shoutoutDetailViewController {
destination.shoutoutSelected = sender as! Int // here sender = ShoutoutSelected, downcast it from Any to your desired type. I am considering destination.shoutoutSelected is of Int type.
}
}
}

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
}

Opening MapKit pin in Maps

I can't seem to get the final part of Ray Wenderlich's mapkit tutorial to work. I get the pins to display correctly but when I click on the pin, it doesn't open in Maps as I would like it too. Here is the link to the full tutorial (Link)
And here is my code:
import Foundation
import UIKit
import MapKit
import AddressBook
class CroquisMapView: UIViewController, MKMapViewDelegate {
var section : Int?
var index : Int?
#IBOutlet weak var mapView: MKMapView!
let regionRadius: CLLocationDistance = 1000
func centerMapOnLocation(location: CLLocation) {
let coordinateRegion = MKCoordinateRegionMakeWithDistance(location.coordinate,
regionRadius * 2.0, regionRadius * 2.0)
mapView.setRegion(coordinateRegion, animated: true)
}
override func viewDidLoad() {
mapView.delegate = self
let coordenadas = croquisGruposArray[section!].items[index!].coordenadas
let coordenadasArray = coordenadas!.characters.split{$0 == ","}.map(String.init)
guard let lat = NSNumberFormatter().numberFromString(coordenadasArray[0])?.doubleValue,
let long = NSNumberFormatter().numberFromString(coordenadasArray[1])?.doubleValue else {
return
}
let initialLocation = CLLocation(latitude: lat, longitude: long)
let artwork = Artwork(title: "\(croquisGruposArray[section!].items[index!].descripcion!)",
locationName: "\(globalLigaNombre!)",
discipline: "Futbol",
coordinate: CLLocationCoordinate2D(latitude: lat, longitude: long))
mapView.addAnnotation(artwork)
centerMapOnLocation(initialLocation)
}
}
class Artwork: NSObject, MKAnnotation {
let title: String?
let locationName: String
let discipline: String
let coordinate: CLLocationCoordinate2D
init(title: String, locationName: String, discipline: String, coordinate: CLLocationCoordinate2D) {
self.title = title
self.locationName = locationName
self.discipline = discipline
self.coordinate = coordinate
super.init()
}
var subtitle: String? {
return locationName
}
func mapItem() -> MKMapItem {
let addressDictionary = [String(kABPersonAddressStreetKey): locationName]
let placemark = MKPlacemark(coordinate: coordinate, addressDictionary: addressDictionary)
let mapItem = MKMapItem(placemark: placemark)
mapItem.name = title
return mapItem
}
func mapView(mapView: MKMapView!, annotationView view: MKAnnotationView!, calloutAccessoryControlTapped control: UIControl!) {
print("CLICKED")
let location = view.annotation as! Artwork
let launchOptions = [MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving]
location.mapItem().openInMapsWithLaunchOptions(launchOptions)
}
}
extension CroquisMapView {
// 1
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
if let annotation = annotation as? Artwork {
let identifier = "pin"
var view: MKPinAnnotationView
if let dequeuedView = mapView.dequeueReusableAnnotationViewWithIdentifier(identifier)
as? MKPinAnnotationView { // 2
dequeuedView.annotation = annotation
view = dequeuedView
} else {
// 3
view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
view.canShowCallout = true
view.calloutOffset = CGPoint(x: -5, y: 5)
view.rightCalloutAccessoryView = UIButton(type: .DetailDisclosure) as UIView
}
return view
}
return nil
}
}
Figured it out: I wasn't calling calloutAccessoryControlTapped in my main class. I added it and it with the code provided by Andrej:
func mapView(mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
let url = NSURL(string: "http://maps.apple.com/?q=\(lat),\(long)")
if UIApplication.sharedApplication().canOpenURL(url!) == true {
UIApplication.sharedApplication().openURL(url!)
}
}
You can try this:
let url = NSURL(string: "http://maps.apple.com/?q=44.33833,13.98131")
if UIApplication.sharedApplication().canOpenURL(url!) == true
{
UIApplication.sharedApplication().openURL(url!)
}
You can also do like this:
extension ViewController : MKMapViewDelegate {
func getDirections(){ // Here you can put everything, this function is calling when the user select the dialogue pin's bubble. I propose a code who open the application Map.
guard let selectedPin = selectedPin else { return }
let mapItem = MKMapItem(placemark: selectedPin)
let launchOptions = [MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving]
mapItem.openInMapsWithLaunchOptions(launchOptions)
}
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView?{
guard !(annotation is MKUserLocation) else { return nil }
if annotation is MKUserLocation {
//return nil so map view draws "blue dot" for standard user location
return nil
}
let reuseId = "pin"
var pinView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId) as? MKPinAnnotationView
if pinView == nil {
pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
}
pinView?.pinTintColor = UIColor.orangeColor()
pinView?.canShowCallout = true
let smallSquare = CGSize(width: 30, height: 30)
let button = UIButton(frame: CGRect(origin: CGPointZero, size: smallSquare))
button.setBackgroundImage(UIImage(named: "car"), forState: .Normal)
button.addTarget(self, action: #selector(self.getDirections), forControlEvents: .TouchUpInside)
pinView?.leftCalloutAccessoryView = button
return pinView}
}