MapKit Tutorial: Getting Started

Hello! Can you help please? How can i add hyperlink in subtitle?

http://candycode.io/support-links-in-text-on-ios/

Can you show example in annotation please?

try it on your own; if you need help, contact Jure Zove (the author of the linked candycode article) — he’s super helpful and really knows his stuff!

Thank you!
Can i make second subtitle under the first subtitle in annotation?

Insert a new-line character into subtitle, for example:

var subtitle: String? {
  return locationName + "\n2nd subtitle"
}

And, in viewFor annotation customise the detailCalloutAccessoryView, like in my December post above:

let detailLabel = UILabel()
detailLabel.numberOfLines = 0
detailLabel.text = annotation.subtitle
view.detailCalloutAccessoryView = detailLabel

I tried like in your December post, but all annotations after that messed up. I make program with classes like in your part “Showing an Artwork on the Map”, without JSON.

I want to make big annotation. I have tried to add label in annotation. But after that all mixed up. For example: first place on the mape(titile: market1, subtitle: candyshop, coordinate: 1,1), second place on the map((titile: market2, subtitle: carwash, coordinate: 2,2)), thied place on the map(titile: market3, subtitle: restaurant, coordinate: 3,3)
After i add
let detailLabel = UILabel ()
detailLabel.numberOfLines = 0
detailLabel.text = annotation.subtitle
view.detailCalloutAccessoryView = detailLabel
first place on the mape(titile: market1, subtitle: restaurant, coordinate: 1,1), second place on the map((titile: market2, subtitle: candyshop, coordinate: 2,2)), thied place on the map(titile: market3, subtitle: carwash coordinate: 3,3).
Subtitles mixed.
Sorry for my bad English))

This is my annotation:

func mapView(_ mapView: MKMapView,
             viewFor annotation: MKAnnotation) -> MKAnnotationView? {
    if let annotation = annotation as? Wash {
        let identifier = "Pin"
        var view: MKAnnotationView
        if let dequeuedView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier) { // 2
            dequeuedView.annotation = annotation
            view = dequeuedView
        } else {
            // 3
            view = MKAnnotationView(annotation: annotation, reuseIdentifier: identifier)
            view.canShowCallout = true
            view.calloutOffset = CGPoint(x: -5, y: 5)
            let smallSquare = CGSize(width: 35, height: 35)
            let button = UIButton(frame: CGRect(origin: CGPoint.zero, size: smallSquare))
            button.setBackgroundImage(UIImage(named: "Navigation"), for: UIControlState())
            view.leftCalloutAccessoryView = button
            let detailLabel = UILabel()
            detailLabel.numberOfLines = 0
            detailLabel.text = annotation.subtitle
            view.detailCalloutAccessoryView = detailLabel
            

                     }
        view.image = annotation.pinImage()
        return view
    }
    return nil
}

this is my class:

class Wash: 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 pinImage() -> UIImage  {
    switch discipline {
    default:
        return UIImage(named: "Wash")!
    }
}

// annotation callout opens this mapItem in Maps app
func mapItem() → MKMapItem {
let addressDict = [CNPostalAddressStreetKey: subtitle!]
let placemark = MKPlacemark(coordinate: coordinate, addressDictionary: addressDict)

    let mapItem = MKMapItem(placemark: placemark)
    mapItem.name = title
    
    return mapItem
}

}

this is my array:

let washs: [Wash] = [

        //Москва
        Wash(title: "Мойка", locationName: "ул. Ленинский проспект 1",
             discipline: "", coordinate: CLLocationCoordinate2D(latitude: 55.670584, longitude: 37.737914)),
        Wash(title: "Мойка",
             locationName: "Игральная 10",
             discipline: "",
             coordinate: CLLocationCoordinate2D(latitude: 54.900991, longitude: 38.080399))

]

oops! the detailLabel.text customisation must also happen when you reuse a dequeued view, otherwise, it keeps the text from when it was created.

To fix this, move the detailLabel declaration to just below var view, and copy the detailLabel lines into the dequeuedView setup, changing view.detailCalloutAccessoryView to dequeuedView.detailCalloutAccessoryView:

if let annotation = annotation as? Artwork {
  let identifier = "artPin"
  var view: MKPinAnnotationView
  let detailLabel = UILabel()
  if let dequeuedView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier)
    as? MKPinAnnotationView { // 2
    dequeuedView.annotation = annotation
    detailLabel.numberOfLines = 0
    detailLabel.text = annotation.subtitle
    dequeuedView.detailCalloutAccessoryView = detailLabel
    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

    detailLabel.numberOfLines = 0
    detailLabel.text = annotation.subtitle
    view.detailCalloutAccessoryView = detailLabel
  }
  view.pinTintColor = annotation.pinTintColor()
  return view
}

ohhh, it works!!! Thank you very much!!! You are great person!!! You really helped me.

Hi, Audrey!
How can i put a phone number in a map annotation so i can tap on it and call this number?
Can you help me?
Thanks! :slight_smile:

perhaps a combination of Jure’s NSAttributedString
http://candycode.io/support-links-in-text-on-ios/

and the answer at

let url = NSURL(string: "tel://1234567890")!
UIApplication.sharedApplication().openURL(url)
1 Like

Thanks a lot!

But how can i do this in a lot of subtitles with a lot of numbers which i can tap?
And where i must put this code?

Sorry but i really don’t understand this :sweat:

Hi. I’m trying to build a similar app but the locations coordinates are not separate but as one string. How to get these location coordinates in the fromJson func?

This is the JSON dataset: https://data.honolulu.gov/api/views/m2gw-xt7z/rows.json?accessType=DOWNLOAD

Can I get that by changing slightly in your code:

let latitude = (json[21].string! as NSString).doubleValue
let longitude = (json[21].string! as NSString).doubleValue
let coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)

If yes, how?

hi Naveen: it looks to me like your coordinates are in json[10]

let str = "21.311542, -157.862936"
let numbers = str.components(separatedBy: CharacterSet(charactersIn: ", "))

numbers is the array [“21.311542”, “”, “-157.862936”] so your coordinates (as Strings) are numbers.first and numbers.last

1 Like

Thx. But when I tried to convert them to double to use them in CLLocationCoordinate2D, I couldn’t. What would be the mistake?? How to convert lat and long strings to double??

'let str = (json[10].string! as NSString)
let numbers = str.components(separatedBy: CharacterSet(charactersIn: ", "))
let latitude = (numbers.first)
let longitude = (numbers.last)
let coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude) ’

extract the two substrings, then convert each substring to Double

let numbers = str.components(separatedBy: CharacterSet(charactersIn: ", "))
let latString = numbers.first 
let longString = numbers.last 
let latitude = (latString! as NSString).doubleValue
let longitude = (longString! as NSString).doubleValue
let coordinate ...

I have tried both NSString and Number formatter but What I get is the execution error right at this line: let str = (json[10].string! as NSString)

My lat and long values always return 0 or nil. Its like it won’t even take the values.

What does this error mean: EXC_BAD_INSTRUCTION(code=EXC_1386_INVOP, subcode=0x0)

the first item in data — Aloha Tower Drive — and possibly other items, doesn’t have any coordinates; you’ll have to check there’s actually a string there, or it might be easier to manually change the null to “” (empty string) then test for that. Or manually delete those items that don’t have coordinates.

edited to add: JSON.swift returns nil if it can’t create a String from json[10].string so do something like this:

if let str = json[10].string {
  let numbers = ...
  ...
}
1 Like