In the last lesson, we explored the joys of making maps using the UIMapView
and MapKit
. While showing maps is great, we often want to add things to a map, such as locations and graphics. These are known as Annotations and Overlays. In this lesson we’ll make some of each. We’ll start by locating pizza restaurants in Chicago, then adding a 2 kilometer pizza delivery region for a pizza chain using overlays. Finally we’ll map out how to get from the former Marshall Field’s (now Macy’s) to the first Deep Dish pizza.
Make a New Project
I’ll quickly go through the the basic setup of a map. For more detail see the previous lesson. Start by making a New Single-view project Called MapAnnotationsOverlayDemo. Use a Universal device and Swift as the language. When it loads, we need to turn on MapKit
. In the Project Properties, click on the the Capabilities tab. You will see a list of functions we can use. About halfway down the list you’ll See Maps
Turn on maps and the menu opens.
We won’t be changing anything here, but the framework will load when we need it.
Go to the storyboard. In the Object Library, find the Map Kit View object
Drag it somewhere on the story board.
Select the Map view. Click the button in the Auto Layout menu on the lower right of the storyboard. This will bring up the the pin menu. Click off Constrain to margins. Set the margins to be 0 on all sides. Change the Update Frames to Items of New Constraints. Your pin menu should look like this.
Click Add 4 Constraints. The map view takes the entire view on the story board.
With the map view still selected, look at the attribute inspector. At the top is some attributes specific to map views:
Turn off Points of Interest. They will get in the way of our app.
Open the Assistant editor. Go to the ViewController.swift file. Before we do anything else, we needto import MapKit.
It is a separate framework than UIKit
. Add import
MapKit
import UIKit import MapKit
Control-drag from the map view on the storyboard to the code. Add the outlet mapView. We also need to add the delegate MKMapViewDelegate
. Just to keep things organized, I added a few marks. When done your ViewController
code should look like this:
//MARK: Global Declarations class ViewController: UIViewController,MKMapViewDelegate { //MARK: Properties and Outlets @IBOutlet weak var mapView: MKMapView! //MARK: - Annotations //MARK: - Overlays //MARK: - Map setup //MARK: Life Cycle override func viewDidLoad() { super.viewDidLoad() } }
There’s a coordinate we’ll use as a reference point for the city of Chicago. The city, and some of the suburbs use the same grid system for addresses. There is an origin point for this grid in downtown Chicago, where we can say we are at 0,0. That is the intersection of State and Washington Streets. We’ll use it in a few places, thus I’ll use a global constant. Add this code above the class definition for ViewController
just below the Global Declarations
mark:
//MARK: Global Declarations let chicagoCoordinate = CLLocationCoordinate2DMake(41.8832301, -87.6278121)// 0,0 Chicago street coordinates
Add the following method to ViewController
to set the initial region for our app with 5 kilometers (3 miles) boundaries:
//MARK: - Map setup func resetRegion(){ let region = MKCoordinateRegionMakeWithDistance(chicagoCoordinate, 5000, 5000) mapView.setRegion(region, animated: true) }
Add the region to viewDidLoad
:
override func viewDidLoad() { super.viewDidLoad() resetRegion() }
Build and run. You get a map of downtown Chicago.
Adding a Single Annotation Pin
The most common and simplest annotation is a pin. There is a special class of annotations MKPinAnnotationView
for displaying pins. This is a subclass of the more generic MKAnnotationView
, which uses a custom class that adopts the MKAnnotation
Protocol for its data source. Between the chicagoCoordinate
and the ViewController
declarations, add this class:
class ChicagoCenterCoordinate: NSObject,MKAnnotation{ var coordinate: CLLocationCoordinate2D = chicagoCoordinate var title: String? = "0,0 Street Numbers" }
Any object can be an annotation object. Here we have a NSObject
adopting the MKAnnotation
protocol. Unlike other protocols, MKAnnotation
does not have required methods, but required properties. You must declare a property coordinate
. You can optionally declare title
and subtitle
as well. If you have other information, you can pass that along too.
The annotation must be added to mapView
. Change viewDidLoad
like this:
override func viewDidLoad() { super.viewDidLoad() resetRegion() mapView.addAnnotation(ChicagoCenterCoordinate()) }
Build and run. You get a pin at the center coordinates:
Tap the pin and you get a title.
Using More Than One Pin
If you had only one annotation, this would be an acceptable way of using them. However you will likely have many annotations. Annotations work like table views, they reuse existing annotations to conserve resources. Unlike table views, they can work automatically if you want the default set of features.
A New Data Set
We’ll need a new data set of several locations. In this demo We’ll use a few deep dish pizza restaurants in Chicago. Press Command-N and make a new Cocoa Touch Class named PizzaLocation.swift, subclassing NSObject
. Add the following code to the new class:
import UIKit import MapKit class PizzaLocation: NSObject, MKAnnotation{ var identifier = "pizza location" var title: String? var coordinate: CLLocationCoordinate2D init(name:String,lat:CLLocationDegrees,long:CLLocationDegrees){ title = name coordinate = CLLocationCoordinate2DMake(lat, long) } }
Once again we import MapKit
to get the data types we need for locations. In line 3, We add the MKAnnotation
protocol to the class. This time we have three properties: the required coordinate
, title
, and the reuse identifier
for this annotation. In lines 7 – 10 we have an initializer taking a name, a latitude and longitude to populate the title
and coordinate
for each instance of this object.
This is the code for the annotation. Usually, you would have this class read an internal file or get annotation from a server. However, I want to keep this simple and on topic, so I’ll make a constant array for our annotations. Under this class we’ll add one more class. In this class, we’ll have one property: an array of PizzaLocation
s. Add this(which I seriously suggest cutting and pasting):
class PizzaLocationList: NSObject { var restaurant = [PizzaLocation]() override init(){ restaurant += [PizzaLocation(name:"Connie's Pizza",lat:41.84937922,long:-87.6410584 )] restaurant += [PizzaLocation(name:"Connie's Pizza",lat:41.90146341,long: -87.62848137 )] restaurant += [PizzaLocation(name:"Connie's Pizza",lat:41.85110748,long: -87.61286663 )] restaurant += [PizzaLocation(name:"Connie's Pizza",lat:41.89224916,long:-87.60951805 )] restaurant += [PizzaLocation(name:"Giordano's",lat:42.00302015,long:-87.81630768 )] restaurant += [PizzaLocation(name:"Giordano's",lat:41.79915575,long:-87.59028088)] restaurant += [PizzaLocation(name:"Giordano's",lat:41.85776469,long:-87.66138509)] restaurant += [PizzaLocation(name:"Giordano's",lat:41.95296188,long:-87.77541371)] restaurant += [PizzaLocation(name:"Pequod's",lat:41.92185084,long:-87.66451631)] restaurant += [PizzaLocation(name:"Pizzeria DUE",lat:41.89318499,long:-87.62661003)] restaurant += [PizzaLocation(name:"Pizzeria UNO",lat:41.8924923,long:-87.626859)] restaurant += [PizzaLocation(name:"Gino's East",lat:41.8959379,long:-87.6229284)] restaurant += [PizzaLocation(name:"Lou Malnati's",lat:41.95340615,long:-87.73214376)] restaurant += [PizzaLocation(name:"Lou Malnati's",lat:41.87169869,long:-87.62737565)] restaurant += [PizzaLocation(name:"Lou Malnati's",lat:41.96074325,long:-87.6835484)] restaurant += [PizzaLocation(name:"Lou Malnati's",lat:41.88411358,long:-87.65808167)] restaurant += [PizzaLocation(name:"Lou Malnati's",lat:41.85186556,long:-87.72202439)] restaurant += [PizzaLocation(name:"Lou Malnati's",lat:41.90239382,long:-87.62846892)] restaurant += [PizzaLocation(name:"Lou Malnati's",lat:41.89031406,long:-87.63388913)] restaurant += [PizzaLocation(name:"Lou Malnati's",lat:41.92911995,long:-87.65359186)] restaurant += [PizzaLocation(name:"Lou Malnati's",lat:41.9089214,long:-87.6775678)] } }
I did the work of looking up all those locations. The initializer here populates the array restuarant
.
Adding an Array of Annotations to the Map
Go back to ViewController
. Add a constant restaurants
to the class
let restaurants = PizzaLocationList().restaurant
Change viewDidLoad
to this:
override func viewDidLoad() { super.viewDidLoad() resetRegion() mapView.addAnnotation(ChicagoCenterCoordinate()) mapView.addAnnotations(restaurants) }
We added the addAnnotation
counterpart addAnnotations
, which takes an array of MKAnnotation
s instead of a single annotation.
Build and run. You get a series of pins.
Head south and you’ll find less pins. Click on the one I did and you’ll find it is Connie’s Pizza.
Using the Map Kit Delegate for Annotations
Annotations default to a red pin with a call out for the title and subtitle. If all you want to do is put a set of red pins on the map, populate the array annotation
with objects adopting the MKAnnotation
protocol. You’ll notice however you can’t tell what these pins are without clicking them. To customize the annotations in any way, you’ll need a method in MKMapViewDelegate
. The delegate method mapview:viewForAnnotation
works like tableview:cellForRowAtIndexPath
works for tables. It creates the annotations you need at any given time. Add this to the ViewController
class
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? { var view : MKPinAnnotationView guard let annotation = annotation as? PizzaLocation else {return nil} if let dequeuedView = mapView.dequeueReusableAnnotationViewWithIdentifier(annotation.identifier) as? MKPinAnnotationView { view = dequeuedView }else { //make a new view view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: annotation.identifier } return view }
The function mapview:viewForAnnotation
has a map view and an annotation for parameters, which we return a optional MKAnnotoationView
. nil
means we don’t have a pin here for this annotation for some reason. MKAnnotationView
is a UIView
for a more flexible annotation. For convenience MapKit
has a subclass of MKAnnotationView
named MKPinAnnotationView
which sets a pin at a annotation’s location.
We start this function with declaring a view of class MKPinAnotationView
, since they are the easiest type of annotation view. I’m a bit careful about my annotations, and use guard
to make sure I get the correct type of annotation, a PizzaLocation
. Like table cells, annotations dequeue. For that to work properly we need an identifier, which we find in annotation.identifier
. I check for an existing dequeued view, using that identifier
property of PizzaLocation
. If the annotation has already been created I assign it to the view. If it isn’t I can make a new view, assigning the reuse identifier to the annotation view.
As this is a delegate, don’t forget to set the delegate in viewDidLoad
.
mapkit.delegate = self
Build and run. You get the same map as before…almost.
You cannot get the title in a call out if you click on the pin. Once we start making our own MKAnnotationview
s, we have to specify how we want annotation view to look and behave.
Changing Pin Color
Starting in iOS9, we can make the pin color any color we wish using the pinTintColor
property of MKPinAnnotation
. Before we do, add this function to ViewController
func pinColor(name:String) -> UIColor{ var color = UIColor.purpleColor() switch name{ case "Connie's Pizza": color = UIColor.blueColor() case "Lou Malnati's": color = UIColor.greenColor() case "Giordano's": color = UIColor.yellowColor() default: color = UIColor.orangeColor() } return color }
This changes the color of the pin based on the pizza chain. Just before the return view
in mapview:viewForAnnotation
add the highlighted line:
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? { var view : MKPinAnnotationView guard let annotation = annotation as? PizzaLocation else {return nil} if let dequeuedView = mapView.dequeueReusableAnnotationViewWithIdentifier(annotation.identifier) as? MKPinAnnotationView { view = dequeuedView }else { view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: annotation.identifier) } view.pinTintColor = pinColor(annotation.title!) return view }
Once we get the view, we set the pin color. Build and run. Now the pins are colorful.
One thought about style and user interfaces when changing the pin colors. Looking at these pins can you tell which pins are Lou Malnati’s? Without a key on the map it’s difficult. Use color apraingly and for uses that color makes sense. If I was rating these restaurants. using red, green and and yellow tints tell me which ones to go to and which ones to avoid
Multiple Classes of Annotations
You’ll see one red pin. In our code, we never specified a red pin. Why it it there? Zoom in on the location (option-drag in the simulator) and you’ll find it is the center coordinate of State and Washington streets.
Look back a the delegate methods and find this line of code:
guard let annotation = annotation as? PizzaLocation else {return nil}
This code returns nil
if we cannot downcast to PizzaLocation
, which we can’t with class ChicagoCenterCoordinate
. For any nil
annotation view, we get our default red pin. We need to change the code to downcast for ChicagoCenterCoordinate
, then set properties on that pin. Change the code to this:
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? { if let annotation = annotation as? PizzaLocation{ if let view = mapView.dequeueReusableAnnotationViewWithIdentifier(annotation.identifier) as? MKPinAnnotationView { return view }else{ let view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: annotation.identifier) view.pinTintColor = pinColor(annotation.title!) return view } }else{ if let annotation = annotation as? ChicagoCenterCoordinate{ if let view = mapView.dequeueReusableAnnotationViewWithIdentifier("center") as? MKPinAnnotationView { return view }else { let view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "center") view.pinTintColor = UIColor.blackColor() return view } } } return nil }
We test for an annotation to cast properly to ChicagoCenterCoordinate
, and then make a black pin if necessary. Build and run. You now have the black pin.
Customizing the Annotation
There are several properties in MKAnnotationView
we can use to customize the annotation view. We can get back the call out with two lines of code. Add this under the pinTintColor
of black.
view.pinTintColor = UIColor.blackColor() view.enabled = true view.canShowCallout = true
Build and Run. Click the black pin and you get a call out.
We can customize the appearance of the pin. The pin is a UIImage
. We can add an image to the annotation in its image
property. Here are two images we can use for the annotations we have
Right click each and save the images as pizza pin and crosshairs respectively.
Go to Assets.xcassets
. From where you saved the images, drag the pizza pin into the assets folder, so it creates a new image.
Check the image set attributes to make sure this image set has the name pizza pin.
Do the same for the cross hairs, making sure the image set name is crosshairs.
Add two news constants to the beginning of the ViewController
code. This speeds loading the image later in the delegate.
let pizzaPin = UIImage(named: "pizza pin") let crossHairs = UIImage(named: "crosshairs")
Change the code for mapView:viewForAnnotation:
to this
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? { if let annotation = annotation as? PizzaLocation{ if let view = mapView.dequeueReusableAnnotationViewWithIdentifier(annotation.identifier){ return view }else{ let view = MKAnnotationView(annotation: annotation, reuseIdentifier: annotation.identifier) view.image = pizzaPin view.enabled = true view.canShowCallout = true view.leftCalloutAccessoryView = UIImageView(image: pizzaPin) return view } }else{ if let annotation = annotation as? ChicagoCenterCoordinate{ if let dequeuedView = mapView.dequeueReusableAnnotationViewWithIdentifier("center"){ return dequeuedView }else { let view = MKAnnotationView(annotation: annotation, reuseIdentifier: "center") view.image = crossHairs view.enabled = true view.canShowCallout = true return view } } } return nil }
Lines 3,6,14,and 18 usesMKAnnotationView
instead of MKPinAnnotationView
in order to use a custom image. A pin will replace your image if you use MKPinAnnotationView
In line 7, we set the image
property of the annotation view to our pizza pin. Similiarly line 19 add our crosshairs. Lines 8 and 9 enable the call out with the title. Line 10 adds a view to the callout. This can be any subclass of UIView
, including controls such as buttons. We make a UIImageView
from the pizza pin image.
Build and run. You get the pizza image instead of the pin, with cross hairs at State and Washington streets.
Click a Pizza restaurant and you get its name and the pizza icon
Working with Overlays
In some cases we don’t need a point for an annotation but an area, line or tile. To show those on a map, we use an overlay. Overlays set up a lot like annotations. We start with a overlay object and in the MapKitDelegate
method for overlays, tell the system what overlay view goes where.
Overlays can have several standard shapes.You can use circles, polygons, and polylines as built in shapes, plus the tile overlay. We’ll look at the polyline and circle for our examples.
Circle Overlays
Suppose we want to show a delivery area based on a distance from the restaurant. We can display that with a circle for the delivery area. We need an instance of the MKCircle
class to do that. MKCircle
has several initializers depending on your measurement system. We’re sticking to coordinates, so we’ll use the coordinate of the restaurant as our center and a distance in meters which becomes the radius of our circle. Add this code:
func deliveryOverlay(restaurantName:String, radius:CLLocationDistance){ for restaurant in restaurants{ if restaurant.title == restaurantName{ let center = restaurant.coordinate let circle = MKCircle(centerCoordinate: center, radius: radius) mapView.addOverlay(circle) } } }
We do a linear search for the specific restaurant, adding the circle overlay to mapView
if it is the restaurant we are looking for. We’ll also need the delegate method to make a overlay renderer, just like an annotation needed an annotation view. Add this code to ViewController
:
func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOverlayRenderer { if overlay.isKindOfClass(MKCircle){ let circleRenderer = MKCircleRenderer(overlay: overlay) circleRenderer.fillColor = UIColor.blueColor().colorWithAlphaComponent(0.1) circleRenderer.strokeColor = UIColor.blueColor() circleRenderer.lineWidth = 1 return circleRenderer } return MKOverlayRenderer(overlay: overlay) }
This delegate method returns a MKOverlayRenderer
, from the MKOverlay
parameter. MKOverlayRenderer
is a form of UIView, which means most core graphics properties and function work on it. Since we may have more than one subclass of MKOverlay in the delegate, Line 2 checks if we have an MKCircle
before we make it a MKCircleRenderer
. Lines 3 through 5 set the color and stroke of the circle. Line 6 returns our circle to the map for rendering. In case this was not a circle, we have a catch all return
to end the code.
Add the function at 2 Kilometers for Connie’s pizza into viewDidLoad
. This really isn’t their delivery area, we’re making this up for the example.
override func viewDidLoad() { super.viewDidLoad() resetRegion() mapView.addAnnotation(ChicagoCenterCoordinate()) mapView.addAnnotations(restaurants) deliveryOverlay("Connie's Pizza",radius: 2000) mapView.delegate = self }
Build and run. You get the circles, but they are a bit big for our view.
Zoom out a little (use the option key to pinch on the simulator) to see the full circles.
Rendering Polylines in Overlays
Both poly lines and polygons are an array of coordinates making up a line. The difference is the polygon closes the shapes and allows filling while the the polyline doesn’t Polylines are the way we mark routes on a map.
Unlike the last lesson, the only Chicago trivia I’ve given is the address center of Chicago at State and Washington streets. Let’s make a walking path from the center of the addresses to the center of the Deep Dish pizza universe: Pizzeria Uno. There’s plenty of debate wiether it was Owner Ike Sewell or Chef Rudy Malnati who invented deep dish pizza, but it’s clear this was the restaurant that it happened. As you can tell from the pins on our map, Rudy’s son Lou (who was also a manager at UNO’s) started his own chain first in the North and northwest suburbs and then pretty much spread to most of Chicago. I didn’t include all 45 Chicago area Malnati’s locations in this data set. Lou Malnati’s #46 in Phoenix Arizona opens the same day I’m posting this.
One of the great things about Chicago is the grid system. We need to walk in one direction for a while, make a turn and then walk in the new direction. Without using the directions API, we can get there with only three coordinates. We already have two, and I’ll give you the third. Add this function to our code:
func UnoDirections(){ var coordinates = [CLLocationCoordinate2D]() coordinates += [ChicagoCenterCoordinate().coordinate] //State and Washington coordinates += [CLLocationCoordinate2D(latitude: 41.8924847, longitude: -87.6280187)] coordinates += [restaurants[10].coordinate] //Uno's let path = MKPolyline(coordinates: &coordinates, count: coordinates.count) mapView.addOverlay(path) }
MKPolyline
and MKPolygon
need an array of CLLocationCoordinate2D
. We make that array by taking our center coordinate, the coordinate of the traffic intersection we make our turn and the restaurant coordinates. We get a MKPolyLine
object path
from the coordinates and a count of the number of coordinates. You’ll notice we used &coordinates
and not coordinates
. This parameter takes the array as an evil (or at least dangerous) UnsafeMutablePointer
. We don’t pass values to this, we share them with an &
. Once we have the path, we add it to the overlay
array in mapView
.
Next we make a few additions to the delegate method.
func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOverlayRenderer { if overlay.isKindOfClass(MKCircle){ let circleRenderer = MKCircleRenderer(overlay: overlay) circleRenderer.fillColor = UIColor.blueColor().colorWithAlphaComponent(0.1) circleRenderer.strokeColor = UIColor.blueColor() circleRenderer.lineWidth = 1 return circleRenderer } if overlay.isKindOfClass(MKPolyline){ let polylineRenderer = MKPolylineRenderer(overlay: overlay) polylineRenderer.strokeColor = UIColor.blueColor() polylineRenderer.lineWidth = 2 return polylineRenderer } return MKOverlayRenderer(overlay: overlay) }
The MKpolylineRenderer
works much like the MKCircleRenderer
. We check if overlay
is the correct class. If so, we get an instance of the renderer, and set some properties of the line. In this case, we have no fill.
Add UnoDirections
to viewDidload
:
UnoDirections()
Build and run. You’ll see a line on the map now.
Since Uno’s is only a block east of State Street. We are losing our line under the annotations. Zoom in on the map and you can see it better.
Where to Go from Here
This is the basics of annotations and overlays. From this you can do a lot, as long as you have good coordinate data. What I’ve covered in this and the last lesson was how to make a user interface with the map. What I didn’t cover here was any of the core location stuff. I didn’t tell you how to find the location of the device, nor did I show you how to get directions. Those are lessons unto themselves.
I’m going to use maps and tables in upcoming lessons to discuss the form for large data sets, such as JSON and XML. You’ll see more about annotations and the like in upcoming lessons as we get data from external sources.
The Whole Code
ViewController.swift
// // ViewController.swift // MapAnnotationsOverlayDemo // // Created by Steven Lipton on 5/10/16. // Copyright © 2016 MakeAppPie.Com. All rights reserved. // import UIKit import MapKit //MARK: Global Declarations let chicagoCoordinate = CLLocationCoordinate2DMake(41.8832301, -87.6278121)// 0,0 Chicago street coordinates class ChicagoCenterCoordinate: NSObject,MKAnnotation{ var coordinate: CLLocationCoordinate2D = chicagoCoordinate var title: String? = "0,0 Street Numbers" } class ViewController: UIViewController,MKMapViewDelegate { //MARK: Properties and outlets let pizzaPin = UIImage(named: "pizza pin") let crossHairs = UIImage(named: "crosshairs") let restaurants = PizzaLocationList().restaurant @IBOutlet weak var mapView: MKMapView! //MARK: - Annotations /* Since the annotations get loaded in viewDidLoad, this is three iterations of the delegate mapView:viewForAnnotation: */ func pinColor(name:String) -> UIColor{ var color = UIColor.purpleColor() switch name{ case "Connie's Pizza": color = UIColor.blueColor() case "Lou Malnati's": color = UIColor.greenColor() case "Giordano's": color = UIColor.yellowColor() default: color = UIColor.orangeColor() } return color } /* Iteration 1 -- single annotation type */ /* func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? { guard let annotation = annotation as? PizzaLocation else {return nil} if let view = mapView.dequeueReusableAnnotationViewWithIdentifier(annotation.identifier) as? MKPinAnnotationView { return view }else { let view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: annotation.identifier) view.pinTintColor = pinColor(annotation.title!) return view } } */ /* Iteration 2 -- two classes of annotation using MKPinAnnotationView */ /* func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? { if let annotation = annotation as? PizzaLocation{ if let view = mapView.dequeueReusableAnnotationViewWithIdentifier(annotation.identifier) as? MKPinAnnotationView { return view }else{ let view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: annotation.identifier) view.pinTintColor = pinColor(annotation.title!) return view } }else{ if let annotation = annotation as? ChicagoCenterCoordinate{ if let view = mapView.dequeueReusableAnnotationViewWithIdentifier("center") as? MKPinAnnotationView { return view }else { let view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "center") view.pinTintColor = UIColor.blackColor() return view } } } return nil } */ /* iteration 3 Using MKAnnotationView and custom images */ func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? { if let annotation = annotation as? PizzaLocation{ if let view = mapView.dequeueReusableAnnotationViewWithIdentifier(annotation.identifier){ return view }else{ let view = MKAnnotationView(annotation: annotation, reuseIdentifier: annotation.identifier) view.image = pizzaPin view.enabled = true view.canShowCallout = true view.leftCalloutAccessoryView = UIImageView(image: pizzaPin) return view } }else{ if let annotation = annotation as? ChicagoCenterCoordinate{ if let dequeuedView = mapView.dequeueReusableAnnotationViewWithIdentifier("center"){ return dequeuedView }else { let view = MKAnnotationView(annotation: annotation, reuseIdentifier: "center") view.image = crossHairs view.enabled = true view.canShowCallout = true return view } } } return nil } //MARK: - Overlays func deliveryOverlay(restaurantName:String, radius:CLLocationDistance){ for restaurant in restaurants{ if restaurant.title == restaurantName{ let center = restaurant.coordinate let circle = MKCircle(centerCoordinate: center, radius: radius) mapView.addOverlay(circle) } } } func UnoDirections(){ var coordinates = [CLLocationCoordinate2D]() coordinates += [ChicagoCenterCoordinate().coordinate] //State and Washington coordinates += [CLLocationCoordinate2D(latitude: 41.8924847, longitude: -87.6280187)] coordinates += [restaurants[10].coordinate] //Uno's let path = MKPolyline(coordinates: &coordinates, count: coordinates.count) mapView.addOverlay(path) } //MARK: Overlay delegate func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOverlayRenderer { if overlay.isKindOfClass(MKCircle){ let circleRenderer = MKCircleRenderer(overlay: overlay) circleRenderer.fillColor = UIColor.blueColor().colorWithAlphaComponent(0.1) circleRenderer.strokeColor = UIColor.blueColor() circleRenderer.lineWidth = 1 return circleRenderer } if overlay.isKindOfClass(MKPolyline){ let polylineRenderer = MKPolylineRenderer(overlay: overlay) polylineRenderer.strokeColor = UIColor.blueColor() polylineRenderer.lineWidth = 2 return polylineRenderer } return MKOverlayRenderer(overlay: overlay) } //MARK: - Map setup func resetRegion(){ let region = MKCoordinateRegionMakeWithDistance(chicagoCoordinate, 5000, 5000) mapView.setRegion(region, animated: true) } //MARK: Life Cycle override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. resetRegion() mapView.addAnnotation(ChicagoCenterCoordinate()) mapView.addAnnotations(restaurants) deliveryOverlay("Connie's Pizza",radius: 2000) UnoDirections() mapView.delegate = self } }
PizzaLocation.swift
// // PizzaLocation.swift // mapAnnotationsDemo // // Created by Steven Lipton on 5/10/16. // Copyright © 2016 MakeAppPie.Com. All rights reserved. // import UIKit import MapKit class PizzaLocation: NSObject, MKAnnotation{ var identifier = "pizza location" var title: String? var coordinate: CLLocationCoordinate2D init(name:String,lat:CLLocationDegrees,long:CLLocationDegrees){ title = name coordinate = CLLocationCoordinate2DMake(lat, long) } } class PizzaLocationList: NSObject { var restaurant = [PizzaLocation]() override init(){ restaurant += [PizzaLocation(name:"Connie's Pizza",lat:41.84937922,long:-87.6410584 )] restaurant += [PizzaLocation(name:"Connie's Pizza",lat:41.90146341,long: -87.62848137 )] restaurant += [PizzaLocation(name:"Connie's Pizza",lat:41.85110748,long: -87.61286663 )] restaurant += [PizzaLocation(name:"Connie's Pizza",lat:41.89224916,long:-87.60951805 )] restaurant += [PizzaLocation(name:"Giordano's",lat:42.00302015,long:-87.81630768 )] restaurant += [PizzaLocation(name:"Giordano's",lat:41.79915575,long:-87.59028088)] restaurant += [PizzaLocation(name:"Giordano's",lat:41.85776469,long:-87.66138509)] restaurant += [PizzaLocation(name:"Giordano's",lat:41.95296188,long:-87.77541371)] restaurant += [PizzaLocation(name:"Pequod's",lat:41.92185084,long:-87.66451631)] restaurant += [PizzaLocation(name:"Pizzeria DUE",lat:41.89318499,long:-87.62661003)] restaurant += [PizzaLocation(name:"Pizzeria UNO",lat:41.8924923,long:-87.626859)] restaurant += [PizzaLocation(name:"Gino's East",lat:41.8959379,long:-87.6229284)] restaurant += [PizzaLocation(name:"Lou Malnati's",lat:41.95340615,long:-87.73214376)] restaurant += [PizzaLocation(name:"Lou Malnati's",lat:41.87169869,long:-87.62737565)] restaurant += [PizzaLocation(name:"Lou Malnati's",lat:41.96074325,long:-87.6835484)] restaurant += [PizzaLocation(name:"Lou Malnati's",lat:41.88411358,long:-87.65808167)] restaurant += [PizzaLocation(name:"Lou Malnati's",lat:41.85186556,long:-87.72202439)] restaurant += [PizzaLocation(name:"Lou Malnati's",lat:41.90239382,long:-87.62846892)] restaurant += [PizzaLocation(name:"Lou Malnati's",lat:41.89031406,long:-87.63388913)] restaurant += [PizzaLocation(name:"Lou Malnati's",lat:41.92911995,long:-87.65359186)] restaurant += [PizzaLocation(name:"Lou Malnati's",lat:41.9089214,long:-87.6775678)] } }
Leave a Reply