Mapkit Pin Annotations with Swift - swift

I am trying to display a map of the world with several pins. When the left button is tapped, it should perform a phone call with each pin associated with a different phone number. I searched around and came up with the following code but it calls the same number. How can I assign a specific phone number to each pin annotation?
import UIKit
import MapKit
import CoreLocation
class CrewDesksViewController: UIViewController, MKMapViewDelegate {
#IBOutlet weak var mapView: MKMapView!
#IBOutlet weak var callCrew: UISegmentedControl!
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
// Ask for Authorisation from the User.
self.locationManager.requestAlwaysAuthorization()
// For use in foreground
self.locationManager.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled() {
// locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.startUpdatingLocation()
}
//Setup our Map View
self.mapView.delegate = self
mapView.showsUserLocation = true
// pin location of different phone numbers for contacting the crewdesk
var location : CLLocationCoordinate2D = CLLocationCoordinate2DMake(50.1166667, 8.6833333)
let pinAnnotation = PinAnnotation()
pinAnnotation.setCoordinate(location)
pinAnnotation.title = "Frankfurt"
pinAnnotation.subtitle = "+496969804620"
self.mapView.addAnnotation(pinAnnotation)
var location2 : CLLocationCoordinate2D = CLLocationCoordinate2DMake(41.8781136, -87.6297982)
let pinAnnotation2 = PinAnnotation()
pinAnnotation2.setCoordinate(location2)
pinAnnotation2.title = "USA"
pinAnnotation2.subtitle = "+1800FLTLINE"
self.mapView.addAnnotation(pinAnnotation2)
var location3 : CLLocationCoordinate2D = CLLocationCoordinate2DMake(48.866667, 2.333333)
let pinAnnotation3 = PinAnnotation()
pinAnnotation3.setCoordinate(location3)
pinAnnotation3.title = "Paris"
pinAnnotation3.subtitle = "+33800900814"
self.mapView.addAnnotation(pinAnnotation3)
var location4 : CLLocationCoordinate2D = CLLocationCoordinate2DMake(51.5085300, -0.1257400)
let pinAnnotation4 = PinAnnotation()
pinAnnotation4.setCoordinate(location4)
pinAnnotation4.title = "London"
pinAnnotation4.subtitle = "+44800896516"
self.mapView.addAnnotation(pinAnnotation4)
var location5 : CLLocationCoordinate2D = CLLocationCoordinate2DMake(50.833333, 4.333333)
let pinAnnotation5 = PinAnnotation()
pinAnnotation5.setCoordinate(location5)
pinAnnotation5.title = "Brussels"
pinAnnotation5.subtitle = "+3980019326"
self.mapView.addAnnotation(pinAnnotation5)
var location6 : CLLocationCoordinate2D = CLLocationCoordinate2DMake(35.685, 139.7513889)
let pinAnnotation6 = PinAnnotation()
pinAnnotation6.setCoordinate(location6)
pinAnnotation6.title = "Japan 日本"
pinAnnotation6.subtitle = "0-0531-12-4066"
self.mapView.addAnnotation(pinAnnotation6)
var location7 : CLLocationCoordinate2D = CLLocationCoordinate2DMake(22.2855200, 114.1576900)
let pinAnnotation7 = PinAnnotation()
pinAnnotation7.setCoordinate(location7)
pinAnnotation7.title = "HongKong"
pinAnnotation7.subtitle = "800-96-5483"
self.mapView.addAnnotation(pinAnnotation7)
var location8 : CLLocationCoordinate2D = CLLocationCoordinate2DMake(52.3740300, 4.8896900)
let pinAnnotation8 = PinAnnotation()
pinAnnotation8.setCoordinate(location8)
pinAnnotation8.title = "Amsterdam"
pinAnnotation8.subtitle = "0-800-022-9324"
self.mapView.addAnnotation(pinAnnotation8)
var location9 : CLLocationCoordinate2D = CLLocationCoordinate2DMake(13.75, 100.516667)
let pinAnnotation9 = PinAnnotation()
pinAnnotation9.setCoordinate(location9)
pinAnnotation9.title = "Bangkok"
pinAnnotation9.subtitle = "01-800-12-066-6078"
self.mapView.addAnnotation(pinAnnotation9)
var location10 : CLLocationCoordinate2D = CLLocationCoordinate2DMake(29.3375, 47.6581)
let pinAnnotation10 = PinAnnotation()
pinAnnotation10.setCoordinate(location10)
pinAnnotation10.title = "Kuwait"
pinAnnotation10.subtitle = "00-1-847-700-9893"
self.mapView.addAnnotation(pinAnnotation10)
var location11 : CLLocationCoordinate2D = CLLocationCoordinate2DMake(1.3667, 103.8)
let pinAnnotation11 = PinAnnotation()
pinAnnotation11.setCoordinate(location11)
pinAnnotation11.title = "Singapore"
pinAnnotation11.subtitle = "800-1204657"
self.mapView.addAnnotation(pinAnnotation11)
var location12 : CLLocationCoordinate2D = CLLocationCoordinate2DMake(-33.8830555556, 151.216666667)
let pinAnnotation12 = PinAnnotation()
pinAnnotation12.setCoordinate(location12)
pinAnnotation12.title = "Sydney"
pinAnnotation12.subtitle = "1-800-1-48557"
self.mapView.addAnnotation(pinAnnotation12)
var location13 : CLLocationCoordinate2D = CLLocationCoordinate2DMake(25.0391667, 121.525)
let pinAnnotation13 = PinAnnotation()
pinAnnotation13.setCoordinate(location13)
pinAnnotation13.title = "Taipei"
pinAnnotation13.subtitle = "0-0801-13-8533"
self.mapView.addAnnotation(pinAnnotation13)
}
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
if annotation is PinAnnotation {
let pinAnnotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "myPin")
pinAnnotationView.pinColor = .Purple
pinAnnotationView.draggable = true
pinAnnotationView.canShowCallout = true
pinAnnotationView.animatesDrop = true
let deleteButton = UIButton.buttonWithType(UIButtonType.Custom) as! UIButton
deleteButton.frame.size.width = 44
deleteButton.frame.size.height = 44
deleteButton.setImage(UIImage(named: "appiconround"), forState: .Normal)
pinAnnotationView.leftCalloutAccessoryView = deleteButton
return pinAnnotationView
}
return nil
}
func mapView(mapView: MKMapView!, annotationView view: MKAnnotationView!, calloutAccessoryControlTapped control: UIControl!) {
if let location = view.annotation as? PinAnnotation {
UIApplication.sharedApplication().openURL(NSURL(string: "tel://+1800FLTLINE")!)
}
}

You're using a class PinAnnotation but didn't provide us with the definition of that class.
I'll assume it's some object that conforms to the MKAnnotation protocol.
You're saving your phone numbers in the subtitle property of your PinAnnotation objects.
In your calloutAccessoryControlTapped method, you get passes the annotation view that the user tapped.
The MKAnnotationView class has a property annotation that holds the annotation object associated with the annotation view.
You need to fetch the annotation from the annotation view, load the subtitle property (which is part of the MKAnnotation protocol) and use that as the phone number to place your phone call.

This is the Pin Annotation class. I understand what you are saying but i am too new in coding to actually translate your observations into code.
import Foundation
import UIKit
import MapKit
class PinAnnotation : NSObject, MKAnnotation {
private var coord: CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: 0, longitude: 0)
var coordinate: CLLocationCoordinate2D {
get {
return coord
}
}
var title: String = ""
var subtitle: String = ""
func setCoordinate(newCoordinate: CLLocationCoordinate2D) {
self.coord = newCoordinate
}
}

Related

Swift - How to update observed data in a (custom or not) MKAnnotation callout WITHOUT deselecting the annotation

I'm struggling to get KVO updates within a callout already displayed.
My use case: I want to display on an open callout the real time distance between user location and the annotation I add to the map. Annotation does not change its position.
I add annotations to mapView, using a custom annotation I have defined. No issue here.
On each annotation selected, the callout displays all the information defined in the custom annotation
However, the distance is refreshed in the callout ONLY if I unselect the annotation and reselect it
The distance property is declared as #objc dynamic so it can be observed.
I compute the distance each time the user location change. This part works too.
I cannot figure out what I'm missing to have the callout updated without closing and reopening it.
The code I'm using is what is described here by Rob: Swift -How to Update Data in Custom MKAnnotation Callout?
So my question: is it possible to change realtime a value (observed) in a notificationView callout ? If yes is KVO the best approach ?
In the link below, how would be implemented the mapView viewFor method ?
Any example would be very helpful.
It's my first post here, so please if I did it wrong, let me know and I will provide more information and details.
But my situation is trivial: the standard callout performs Key-Value Observation (KVO) on title and subtitle. (And the annotation view observes changes to coordinate.). But how to display change of values in the current open callout ? That is the think I do not get.
CustomAnnotation class:
class CustomAnnotation: NSObject, MKAnnotation {
#objc dynamic var title: String?
#objc dynamic var subtitle: String?
#objc dynamic var coordinate: CLLocationCoordinate2D
#objc dynamic var distance: CLLocationDistance
var poiColor: String?
var poiPhone: String?
init(title: String, subtitle: String, coordinate: CLLocationCoordinate2D, poiColor: String, poiPhone: String, distance: CLLocationDistance) {
self.title = title
self.subtitle = subtitle
self.coordinate = coordinate
self.poiColor = poiColor
self.poiPhone = poiPhone
self.distance = distance
super.init()
}
}
CustomAnnotationView class:
class CustomAnnotationView: MKMarkerAnnotationView {
override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
displayPriority = .required
canShowCallout = true
detailCalloutAccessoryView = createCallOutWithDataFrom(customAnnotation: annotation as? CustomAnnotation)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
removeAnyObservers()
}
override var annotation: MKAnnotation? {
didSet {
removeAnyObservers()
if let customAnnotation = annotation as? CustomAnnotation {
updateAndAddObservers(for: customAnnotation)
}
}
}
private var subtitleObserver: NSKeyValueObservation?
private var distanceObserver: NSKeyValueObservation?
private let subtitleLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
private let distanceLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
}
private extension CustomAnnotationView {
func updateAndAddObservers(for customAnnotation: CustomAnnotation) {
subtitleLabel.text = customAnnotation.subtitle
subtitleObserver = customAnnotation.observe(\.subtitle) { [weak self] customAnnotation, _ in
self?.subtitleLabel.text = customAnnotation.subtitle
}
let locationManager = CLLocationManager()
let theLatitude:CLLocationDegrees = (locationManager.location?.coordinate.latitude)!
let theLongitude:CLLocationDegrees = (locationManager.location?.coordinate.longitude)!
// Get pin location
let pointLocation = CLLocation(latitude: customAnnotation.coordinate.latitude, longitude: customAnnotation.coordinate.longitude)
//Get user location
let userLocation = CLLocation(latitude: theLatitude, longitude: theLongitude)
// Return distance en meters
let distanceFromUser = pointLocation.distance(from: userLocation)
customAnnotation.distance = distanceFromUser*100
distanceLabel.text = String(format: "%.03f", customAnnotation.distance)+" cm"
distanceObserver = customAnnotation.observe(\.distance) { [weak self] customAnnotation, _ in
self?.distanceLabel.text = "\(customAnnotation.distance) cm"
}
}
func removeAnyObservers() {
subtitleObserver = nil
distanceObserver = nil
}
func createCallOutWithDataFrom(customAnnotation: CustomAnnotation?) -> UIView {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = true
view.addSubview(subtitleLabel)
view.addSubview(distanceLabel)
NSLayoutConstraint.activate([
subtitleLabel.topAnchor.constraint(equalTo: view.topAnchor),
subtitleLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor),
subtitleLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor),
subtitleLabel.bottomAnchor.constraint(equalTo: distanceLabel.topAnchor),
distanceLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor),
distanceLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor),
distanceLabel.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
if let customAnnotation = customAnnotation {
updateAndAddObservers(for: customAnnotation)
}
return view
}
}
And to finish:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation { return nil }
let annotation = annotation as? CustomAnnotation
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "CustomAnnotation") as? CustomAnnotationView
if annotationView == nil {
annotationView = CustomAnnotationView(annotation: annotation, reuseIdentifier: "CustomAnnotation")
annotationView?.canShowCallout = true
} else {
annotationView?.annotation = annotation
}
return annotationView
}
Thank you.
You would appear to have correctly configured the observers for the subtitle and distance. The problem is that a change in location is not triggering an update to distance. Thus, there is nothing triggering the KVO.
You have an observer for distance, which will trigger an update of the label. But you are not changing distance. You should remove the CLLocationManager code from that routine where you add the observers, and instead create a location manager (not within the annotation view, though) which uses its delegate to update all of the annotation distances, e.g.:
class ViewController: UIViewController {
#IBOutlet weak var mapView: MKMapView!
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
locationManager.distanceFilter = 5
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
}
}
extension ViewController: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let currentLocation = locations.last(where: { $0.horizontalAccuracy >= 0 }) else { return }
mapView.annotations
.compactMap { $0 as? CustomAnnotation }
.forEach {
$0.distance = CLLocation(latitude: $0.coordinate.latitude, longitude: $0.coordinate.longitude)
.distance(from: currentLocation)
}
}
}
Obviously, you would remove the CLLocationManager code from updateAndAddObservers.

Weird behavior of custom annotations views

I've built the same code in SwiftUI and Storyboard and I get the same weird issues.
Whenever I run and go to background and back to foreground, the custom views are lost.
Whenever I zoom-drag far (say 10km), I also lose the non-picture-holding pin, eg "Pencil".
I'm not sure where I've gone amiss, except maybe that I'd need to separate my pins per annotation view "type" (with a given picture, with text...) and set a different reuse identifier based on that, but it sounds weird given I'm not actually creating anything new (only setting properties of the base MKAnnotationView class).
Using the UIKit version as reference:
import UIKit
import MapKit
class MapViewController: UIViewController {
#IBOutlet weak var mapView: MKMapView!
let cityMapManager = CityMapManager()
let cityMapViewDelegate = CityMapViewDelegate()
let places: [Place] = [Place(name: "Cathedral", location: .init(latitude: 50.640364, longitude: 3.062058))]
override func viewDidLoad() {
super.viewDidLoad()
mapView.setRegion(MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 50.640364, longitude: 3.062058), latitudinalMeters: 500.0, longitudinalMeters: 500.0), animated: false)
mapView.showsUserLocation = true
mapView.showsScale = true
mapView.delegate = cityMapViewDelegate
annotateLocalPOI(map: mapView)
}
private func annotateLocalPOI(map: MKMapView) {
var annotation = MKPointAnnotation()
annotation.title = "Red Pin"
annotation.subtitle = "I'm a pin and I'm red. Maybe, or not."
annotation.coordinate = CLLocationCoordinate2D(latitude: 50.64, longitude: 3.06)
// add the annotation to the map (will use red pin, mapView:viewFor: allows changing the display)
map.addAnnotation(annotation)
annotation = MKPointAnnotation()
annotation.title = "Pencil"
annotation.subtitle = "Will not display"
annotation.coordinate = CLLocationCoordinate2D(latitude: 50.642, longitude: 3.059)
map.addAnnotation(annotation)
}
}
struct Place: Identifiable {
let id: UUID = UUID()
let name: String
let location: CLLocationCoordinate2D
}
class CityMapViewDelegate: NSObject, MKMapViewDelegate {
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!.canShowCallout = true
if annotation.title == "Red Pin" {
let pinImage = UIImage(systemName: "timelapse")
annotationView?.image = pinImage
}
if annotation.title == "Pencil" {
// this overrules any subtitle available
annotationView?.detailCalloutAccessoryView = UIImageView(image: UIImage(systemName: "pencil"))
}
} else {
// reset
annotationView?.detailCalloutAccessoryView = nil
annotationView?.image = nil
// set it up again
annotationView?.annotation = annotation
if annotation.title == "Red Pin" {
let pinImage = UIImage(systemName: "timelapse")
annotationView?.image = pinImage
}
if annotation.title == "Pencil" {
// this overrules any subtitle available
annotationView?.detailCalloutAccessoryView = UIImageView(image: UIImage(systemName: "pencil"))
}
}
return annotationView
}
}
class CityLocationManagerDelegate: NSObject, CLLocationManagerDelegate {
var locationStatus = "Status not determined"
var locationFixFound = false
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// We've (at least once) gone under a certain minimum accuracy required to consider we have a "fix"
// we could use that to reflect that information on the UI (color, shape...)
if !locationFixFound {
for location in locations {
if location.horizontalAccuracy < 25.0 {
locationFixFound = true
}
}
}
}
func locationManager(_ manager: CLLocationManager,
didFailWithError error: Error) {
manager.stopUpdatingLocation()
print("Location manager stopped due to error: \(error)")
}
func locationManager(_ manager: CLLocationManager,
didChangeAuthorization status: CLAuthorizationStatus) {
print("Authorization Status changed")
var shouldRequestLocationUpdates = false
switch status {
case CLAuthorizationStatus.restricted:
locationStatus = "Restricted Access to location"
case CLAuthorizationStatus.denied:
locationStatus = "User denied access to location"
case CLAuthorizationStatus.notDetermined:
locationStatus = "Status not determined"
default:
locationStatus = "Allowed to location Access"
shouldRequestLocationUpdates = true
}
if (shouldRequestLocationUpdates == true) {
NSLog("Location to Allowed")
// Start location services
manager.startUpdatingLocation()
} else {
NSLog("Denied access: \(locationStatus)")
}
}
}
struct CityMapManager {
let locationDelegate: CLLocationManagerDelegate
let locationManager: CLLocationManager
init() {
locationDelegate = CityLocationManagerDelegate()
locationManager = CLLocationManager()
locationManager.delegate = locationDelegate
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.requestAlwaysAuthorization()
}
}
and a storyboard that holds a fullscreen map within a class set to MapViewController.

MapKit Display Annotation Clusters and Along With Non-Clustered Annotations

I am new to iOS development and currently using the FBAnnotationClusteringSwift to cluster makers. My app needs to have two annotations which will not be clustered, as they indicate the source and destination addresses. The remaining annotations must be clustered as they represent stations.
What I want to achieve is something like the image bellow, where "A" is my source address, and the clusters represent stations:
However what is happening is, as the clusters are created, the Annotation that represents a non-clustered annotation (the source address) disappears as following:
If the library is mixing all annotations together, that is pretty bad, there should be a way to separate those that I've added through clusteredAnnotationsWithinMapRect (stations) and those markers that were already added before (addresses). Here is the relevant part of the current code:
var markerClusterer: FBClusteringManager?
ViewController ... {
override func viewDidLoad() {
super.viewDidLoad()
...
self.markerClusterer = FBClusteringManager()
self.markerClusterer!.delegate = self
}
func cellSizeFactorForCoordinator(coordinator:FBClusteringManager) -> CGFloat{
return 1.0
}
func mapView(mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
// Here is just like the example code from FBAnnotationClusterSwift
var stations: [MKAnnotation] = fetchStations(...)
if (stations.count > 0) {
NSOperationQueue().addOperationWithBlock({
let scale:Double = Double(self.mapView.bounds.size.width) / self.mapView.visibleMapRect.size.width
self.markerClusterer!.addAnnotations(stations)
var annotationArray = stations
// Needed to do that otherwise the clusters never "explode" into pins
if (scale <= 0.06) {
annotationArray = self.markerClusterer!.clusteredAnnotationsWithinMapRect(self.mapView.visibleMapRect, withZoomScale:scale)
}
self.markerClusterer!.displayAnnotations(annotationArray, onMapView: self.mapView)
})
}
}
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
var reuseId = ""
if (annotation.isKindOfClass(FBAnnotationCluster)) {
reuseId = "Cluster"
var clusterView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId)
if clusterView == nil {
clusterView = FBAnnotationClusterView(annotation: annotation, reuseIdentifier: reuseId, options: nil)
} else {
clusterView!.annotation = annotation
}
return clusterView
} else if (annotation.isKindOfClass(AddressPointAnnotation)) {
reuseId = "AddressPin"
var addressPinView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId)
if addressPinView == nil {
addressPinView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
addressPinView!.canShowCallout = true
}
else {
addressPinView!.annotation = annotation
}
let addressPin = annotation as! AddressPointAnnotation
addressPinView!.image = UIImage(named: addressPin.icon)
return addressPinView
} else if (annotation is Station) {
reuseId = "StationPin"
var stationPinView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId)
if stationPinView == nil {
stationPinView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
}
else {
stationPinView!.annotation = annotation
}
let stationPin = annotation as! Station
stationPinView!.image = UIImage(named: "station")
return stationPinView
} else {
return nil
}
}
}
// Annotation for the stations
class Station: NSObject, MKAnnotation {
var id: Int = 0
var availableBikes: Int = 0
var availableBikeStands: Int = 0
var coordinate: CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: 39.208407, longitude: -76.799555)
}
// Annotations for addresses
class AddressPointAnnotation: MKPointAnnotation {
var icon: String!
}
class Address: NSObject {
var marker: AddressPointAnnotation?
func addMarker(coordinate: CLLocationCoordinate2D) {
marker = AddressPointAnnotation()
marker!.coordinate = coordinate
// ViewController is passed to the Address, so it can add itself to the map
self.controller.mapView.addAnnotation(marker!)
if (self.direction == SOURCE) {
marker!.title = "Starting address"
marker!.icon = "from"
} else {
marker!.title = "Destination address"
marker!.icon = "to"
}
self.controller.mapView.addAnnotation(marker!)
}
}
Any help, idea, or comment is more than welcome. Thanks.

Identify MKPointAnnotation in mapView

I have at least 100 diferents Points ... how can associate each point with the position in my 'listOfPoints' assigning a tag in the position associated in viewForAnnotation .
Here i add my Points, some events will have the same title.
var listOfPoints : Array<Events> = [] // List Of all events
//add points to map
for (index, mPoints) in enumerate(listOfPoints) {
var point: MKPointAnnotation! = MKPointAnnotation()
var location = CLLocationCoordinate2D(latitude: mPoints.latitude, longitude: mPoints.longitude)
point.coordinate = location
point.title = mPoints.name
point.subtitle = mPoints.address
self.mapView.addAnnotation(point)
}
//Draw custom pin in the map
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
print("ffff");
var identifier = "CustomAnnotation"
if annotation.isKindOfClass(MKPointAnnotation) {
var pin = mapView.dequeueReusableAnnotationViewWithIdentifier(identifier)
if pin == nil {
pin = MKAnnotationView(annotation: annotation, reuseIdentifier: identifier)
pin.tag = tagPosition; // here
pin.image = UIImage(named: "mapa_pin")
pin.centerOffset = CGPointMake(0, -10)
pin.canShowCallout = true
var pointTitle = pin!.annotation.title! as String
// Callout
var button = UIButton.buttonWithType(.DetailDisclosure) as UIButton
pin!.leftCalloutAccessoryView = button
var image = UIImageView(image: UIImage(named: "mapa_pin"))
pin!.rightCalloutAccessoryView = image
} else {
pin!.annotation = annotation
}
return pin
}
return nil
}
// Print the position
func mapView(mapView: MKMapView!, didSelectAnnotationView view: MKAnnotationView!) {
println(view.tag);
}
How can associated this tag with the position on my 'listOfPoints'
pin.tag = tagPosition;
or is there another way?
I think that is easy and im doing this in an app my.
I customize a class for Annotation from MKAnnotation:
import MapKit
import AddressBook
class MyAnnotation: NSObject, MKAnnotation
{
let identifier : String
let title: String
let subtitle: String
let coordinate: CLLocationCoordinate2D
let color: MKPinAnnotationColor
init(identifier: String, title: String, subtitle: String, coordinate: CLLocationCoordinate2D, color: MKPinAnnotationColor)
{
self.identifier = identifier
self.title = title
self.subtitle = subtitle
self.coordinate = coordinate
self.color = color
super.init()
}
func mapItem() -> MKMapItem
{
let addressDictionary = [String(kABPersonAddressStreetKey): subtitle]
let placemark = MKPlacemark(coordinate: coordinate, addressDictionary: addressDictionary)
let mapItem = MKMapItem(placemark: placemark)
mapItem.name = title
return mapItem
}
}
Now you can use the field identifier from class MyAnnotation in your points:
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView!
{
if let annotation = annotation as? MyAnnotation
{
let identifier = annotation.identifier
var view: MKPinAnnotationView
if let dequeuedView = mapView.dequeueReusableAnnotationViewWithIdentifier(identifier) as? MKPinAnnotationView
{
view = dequeuedView
view.annotation = annotation
}
else
{
view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
view.canShowCallout = true
view.calloutOffset = CGPoint(x: -5, y: 5)
view.animatesDrop = true
view.leftCalloutAccessoryView = UIButton.buttonWithType(UIButtonType.DetailDisclosure) as! UIView
view.rightCalloutAccessoryView = UIButton.buttonWithType(UIButtonType.ContactAdd) as! UIView
view.pinColor = annotation.color
//view.image
//MKAnnotationView 32 x 29
}
return view
}
return nil
}
If you don't understand you can red this excelente article:
http://www.raywenderlich.com/90971/introduction-mapkit-swift-tutorial
The problem is that you are using a plain vanilla built-in MKPointAnnotation. It has no tag property. You need to define your own MKAnnotation class (i.e. a class that adopts the MKAnnotation protocol). That way, it can have any properties you like.
My very easy solution on Objective-C
.h file:
#import <MapKit/MapKit.h>
#interface mapMKPointAnnotation : MKPointAnnotation
#property (nonatomic) NSInteger tag;
#end
and create object with tag:
mapMKPointAnnotation *annotation = [[mapMKPointAnnotation alloc] init];
[annotation setTag:1];
next get tag in method:
-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
static NSString *SFAnnotationIdentifier = #"SFAnnotationIdentifier";
mapMKPointAnnotation *mMKPAnn = (mapMKPointAnnotation *) annotation;
NSString *img = [mMKPAnn tag] == 0 ? #"map_icon" : #"map_icon_active";

MKPointAnnotations touch event in swift

I would like to know if anyone can tell me how I can touch a pin on the map in the form of MKPointAnnotations .
I would like to click the pin on the map and go to another view by bringing back the variables of the pin that I have preset .
Can anyone explain me this thing in Swift ?
thanks
Edit with code:
class ViewController: UIViewController, MKMapViewDelegate {
#IBOutlet weak var mappa: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
var location : CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: 44.648590, longitude: 10.918794)
let pinAnnotation = PinAnnotation()
pinAnnotation.setCoordinate(location)
self.mappa.addAnnotation(pinAnnotation)
}
class PinAnnotation : NSObject, MKAnnotation {
private var coord: CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: 0, longitude: 0)
var coordinate: CLLocationCoordinate2D {
get {
return coord
}
}
var title: String = "test"
var subtitle: String = "test"
func setCoordinate(newCoordinate: CLLocationCoordinate2D) {
self.coord = newCoordinate
}
}
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
if annotation is PinAnnotation {
let pinAnnotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "myPin")
pinAnnotationView.pinColor = .Purple
pinAnnotationView.draggable = true
pinAnnotationView.canShowCallout = true
pinAnnotationView.animatesDrop = true
let deleteButton = UIButton.buttonWithType(UIButtonType.Custom) as UIButton
deleteButton.frame.size.width = 44
deleteButton.frame.size.height = 44
deleteButton.backgroundColor = UIColor.redColor()
deleteButton.setImage(UIImage(named: "trash"), forState: .Normal)
pinAnnotationView.leftCalloutAccessoryView = deleteButton
return pinAnnotationView
}
return nil
}
func mapView(mapView: MKMapView!, annotationView view: MKAnnotationView!, calloutAccessoryControlTapped control: UIControl!) {
if let annotation = view.annotation as? PinAnnotation {
self.mapView.removeAnnotation(annotation)
}
}
}
Several steps are necessary, here are some code snippets to get you started.
First you need a custom class for your pin annotation which holds the data you want to work with.
import MapKit
import Foundation
import UIKit
class PinAnnotation : NSObject, MKAnnotation {
private var coord: CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: 0, longitude: 0)
private var _title: String = String("")
private var _subtitle: String = String("")
var coordinate: CLLocationCoordinate2D {
get {
return coord
}
}
func setCoordinate(newCoordinate: CLLocationCoordinate2D) {
self.coord = newCoordinate
}
var title: String? {
get {
return _title
}
set (value) {
self._title = value!
}
}
var subtitle: String? {
get {
return _subtitle
}
set (value) {
self._subtitle = value!
}
}
}
Then you need a custom class for your MKMapView which conforms to the MKMapViewDelegate protocol. Implement the method viewForAnnotation there:
import MapKit
import CLLocation
import Foundation
import UIKit
class MapViewController: UIViewController, MKMapViewDelegate {
...
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
if annotation is PinAnnotation {
let pinAnnotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "myPin")
pinAnnotationView.pinColor = .Purple
pinAnnotationView.draggable = true
pinAnnotationView.canShowCallout = true
pinAnnotationView.animatesDrop = true
let deleteButton = UIButton.buttonWithType(UIButtonType.Custom) as UIButton
deleteButton.frame.size.width = 44
deleteButton.frame.size.height = 44
deleteButton.backgroundColor = UIColor.redColor()
deleteButton.setImage(UIImage(named: "trash"), forState: .Normal)
pinAnnotationView.leftCalloutAccessoryView = deleteButton
return pinAnnotationView
}
return nil
}
func mapView(mapView: MKMapView!, annotationView view: MKAnnotationView!, calloutAccessoryControlTapped control: UIControl!) {
if let annotation = view.annotation as? PinAnnotation {
mapView.removeAnnotation(annotation)
}
}
That gives you something like this:
To add a new annotation to your map use this somewhere in your code:
let pinAnnotation = PinAnnotation()
pinAnnotation.setCoordinate(location)
mapView.addAnnotation(pinAnnotation)
Greate work !!! BUT..
I just copy past this and I had to add a few changes. I'll share with you guys those changes.
import MapKit
import Foundation
import UIKit
class PinAnnotation : NSObject, MKAnnotation {
private var coord: CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: 0, longitude: 0)
private var _title: String = String("")
private var _subtitle: String = String("")
var title: String? {
get {
return _title
}
set (value) {
self._title = value!
}
}
var subtitle: String? {
get {
return _subtitle
}
set (value) {
self._subtitle = value!
}
}
var coordinate: CLLocationCoordinate2D {
get {
return coord
}
}
func setCoordinate(newCoordinate: CLLocationCoordinate2D) {
self.coord = newCoordinate
}
}
I hope it will help :D