Active Filters: MapKit

Topics

#70: Custom Map View Pins 🔰

Topics

MKMapView is a powerful class. One great feature is its ability to display custom annotation views for the annotations we use on our maps.

Let's test it out by creating a little treasure map. We'll start by creating a map view and adding an annotation to it:

let mapView = MKMapView(frame: mapFrame)
mapView.delegate = self

let location = CLLocationCoordinate2D(latitude: 37.3317115, longitude: -122.0301835)

let annotation = TreasureAnnotation(coordinate: location)
mapView.addAnnotation(annotation)

view.addSubview(mapView)

Instead of the standard red pin graphic 📍, we'd like to show a fun little treasure chest image at the spot of some secret treasure!

To do this, take advantage of MKAnnotationView's .image property. We can throw a regular UIImage at it to get exactly the behavior we're looking for. All we need to do is implement one delegate function and return an MKAnnotationView from it. MKAnnotationViews get reused just like UITableViewCells and UICollectionViewCells.

func mapView(mapView: MKMapView!, 
  viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
  if (annotation is MKUserLocation) { return nil }

  let reuseID = "chest"
  var v = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseID)

  if v != nil {
    v.annotation = annotation
  } else {
    v = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseID)

    v.image = UIImage(named:"chest")
  }

  return v
}

MKAnnotationView has tons of helpful properties out of the box, be sure to take a look before creating a full-blown subclass.

Topics

#48: MKDirections 🚩

Topics

MKDirections and MKDirectionsRequest are the classes we use to retrieve directions between two locations. They also make it very easy to display the resulting directions route on a map. Let's dive in:

Setup Directions Request

Note: Off camera, we have let the user input a keyword search String, then geocoded it into an MKPlacemark object called placemark.

let request = MKDirectionsRequest()

request.source = MKMapItem.mapItemForCurrentLocation()

let destination = MKPlacemark(
  coordinate: placemark.location.coordinate,
  addressDictionary: nil
)

request.destination = MKMapItem(placemark: destination)
let d = MKDirections(request: request)

d.calculateDirectionsWithCompletionHandler { response, error in
  guard let route = response?.routes.first else { return }

  // we'll do this bit next
}

Display Directions on Map

self.directionsRoute = route

self.mapView?.addOverlay(route.polyline)
self.mapView?.setVisibleMapRect(
  route.polyline.boundingMapRect,
  animated: true
)

Finally, we implement this MKMapViewDelegate method:

func mapView(mapView: MKMapView,
rendererForOverlay overlay: MKOverlay) -> MKOverlayRenderer {
  let renderer = MKPolylineRenderer(
    polyline: directionsRoute.polyline
  )

  renderer.lineWidth = 2
  renderer.strokeColor = UIColor.blueColor()

  return renderer
}

Topics

#47: MKLocalSearch 📍

Topics

MKLocalSearch is a wonderful little gem hidden inside MapKit. It allows an app to perform a natural language query for local points of interest near a given latitude and longitude. Today we'll use it to build a simple app to find us a place to eat lunch.

manager = CLLocationManager()
manager!.delegate = self
manager!.desiredAccuracy = kCLLocationAccuracyThreeKilometers
manager!.requestWhenInUseAuthorization()
manager!.requestLocation()

// then later, once we have a location:

let request = MKLocalSearchRequest()

request.naturalLanguageQuery = "Restaurants"
request.region = MKCoordinateRegionMakeWithDistance(location.coordinate, 1600, 1600)

MKLocalSearch(request: request).startWithCompletionHandler { (response, error) in
  guard error == nil else { return }
  guard let response = response else { return }
  guard response.mapItems.count > 0 else { return }

  let randomIndex = Int(arc4random_uniform(UInt32(response.mapItems.count)))
  let mapItem = response.mapItems[randomIndex]

  mapItem.openInMapsWithLaunchOptions(nil)
}

First we set up our CLLocationManager, and use the new iOS 9 method for requesting a “single location” instead of continuous updates.

Then we create the MKLocalSearchRequest object and tell it to search "Restaurants" within 1600 meters (about a mile) of the user's current location.

After that it's just a matter of waiting for results, choosing one at random, and then we cheat a bit (since this is just a demo) and pop the user out to the Maps app to view the chosen result.

Download the example project here: j.mp/bite047