Tải bản đầy đủ
3 Requesting the User’s Location in the Background

3 Requesting the User’s Location in the Background

Tải bản đầy đủ

Figure 12-2. Enabling location updates in Background Modes in your project
Now import CoreLocation in your code and make your view controller conformant
to CLLocationManagerDelegate. Create your location manager and make sure that
the allowsBackgroundLocationUpdates property is set to true:
lazy var locationManager: CLLocationManager = {
let m = CLLocationManager()
m.delegate = self
m.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
m.allowsBackgroundLocationUpdates = true
return m
}()

When the user presses the button, ask for location updates:
@IBAction func requestBackgroundLocationUpdates() {
locationManager.requestAlwaysAuthorization()
}

Wait until the user accepts the request and then start looking for location updates:
func locationManager(
_ manager: CLLocationManager,
didChangeAuthorization status: CLAuthorizationStatus) {
if case CLAuthorizationStatus.authorizedAlways = status{
manager.startUpdatingLocation()
}

12.3 Requesting the User’s Location in the Background

|

345

}

Last but not least, implement the usual location manager methods to get to know
when the user’s location has changed:
func locationManager(_ manager: CLLocationManager,
didUpdateLocations locations: [CLLocation]) {
// TODO: now you have access to the location--do your work
}
func locationManager(_ manager: CLLocationManager,
didFailWithError error: Error) {
// TODO: handle the error
}

See Also
Recipe 12.2

12.4 Customizing the Tint Color of Pins on the Map
Problem
You want to set the tint color of pin annotations on your map manually.

Solution
Use the pinTintColor property of the MKPinAnnotationView class.

Discussion
Let’s check out an example. Create a single view controller project and dump a map
view on top of your view. Make sure that you set the delegate of this map view to your
view controller. Also link it to a variable named map in your view controller.
In the view controller, we are going to create annotations with reusable identifiers, so
let’s use the color as the ID:
import Foundation
import UIKit
public extension UIColor{
final func toString() -> String{
var red = 0.0 as CGFloat
var green = 0.0 as CGFloat
var blue = 0.0 as CGFloat

346

| Chapter 12: Maps and Location

var alpha = 0.0 as CGFloat
getRed(&red, green: &green, blue: &blue, alpha: &alpha)
return "\(Int(red))\(Int(green))\(Int(blue))\(Int(alpha))"
}
}

Now we create our annotation:
import Foundation
import MapKit
public class
public var
public var
public var

Annotation : NSObject, MKAnnotation{
coordinate: CLLocationCoordinate2D
title: String?
subtitle: String?

public init(coordinate: CLLocationCoordinate2D,
title: String, subtitle: String){
self.coordinate = coordinate
self.title = title
self.subtitle = subtitle
}
}

Now ensure that your view controller conforms to the MKMapViewDelegate protocol,
define the location that you want to display on the map, and create an annotation for
it:
let color = UIColor(red: 0.4, green: 0.8, blue: 0.6, alpha: 1.0)
let location = CLLocationCoordinate2D(latitude: 59.33, longitude: 18.056)
lazy var annotations: [MKAnnotation] = {
return [Annotation(coordinate: self.location,
title: "Stockholm Central Station",
subtitle: "Stockholm, Sweden")]
}()

When your view appears on the screen, add the annotation to the map:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
map.removeAnnotations(annotations)
map.addAnnotations(annotations)
}

12.4 Customizing the Tint Color of Pins on the Map

|

347

And when the map view asks for an annotation view for your annotation, return an
annotation view with the custom color (see Figure 12-3):
func mapView(_ mapView: MKMapView,
viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let view: MKPinAnnotationView
if let v = mapView.dequeueReusableAnnotationView(
withIdentifier: color.toString()), v is MKPinAnnotationView{
view = v as! MKPinAnnotationView
} else {
view = MKPinAnnotationView(annotation: annotation,
reuseIdentifier: color.toString())
}
view.pinTintColor = color
return view
}

Figure 12-3. Our custom color pin is displayed on the map

348

|

Chapter 12: Maps and Location

12.5 Providing Detailed Pin Information with Custom
Views
Problem
When the user taps on an annotation in a map, you want to display details for that
annotation in a view.

Solution
Set the detailCalloutAccessoryView property of your MKAnnotationView instances
to a valid UIView instance.

Discussion
Create your project following the steps outlined in Recipe 12.4. In this recipe, I am
going to reuse a lot of code from the aforementioned recipe, except for the implemen‐
tation of the mapView(_:viewForAnnotation:) delegate method of our view control‐
ler. Instead, we are going to construct instances here of MKAnnotationView and then
set the detail callout accessory view:
func mapView(
_ mapView: MKMapView,
viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
let view: MKAnnotationView
if let v = mapView
.dequeueReusableAnnotationView(withIdentifier: identifier){
// reuse
view = v
} else {
// create a new one
view = MKAnnotationView(annotation: annotation,
reuseIdentifier: identifier)
view.canShowCallout = true
if let img = UIImage(named: "Icon"){
view.detailCalloutAccessoryView = UIImageView(image: img)
}
if let extIcon = UIImage(named: "ExtIcon"){
view.image = extIcon
}
}
return view

12.5 Providing Detailed Pin Information with Custom Views

|

349

}

Figure 12-4 shows the image of an annotation on a map. The image inside the callout
is the detail callout accessory view.

Figure 12-4. Annotation with detail callout accessory
I am using two public domain images in this recipe. You also can
find public domain images on Google.

12.6 Displaying Traffic, Scale, and Compass Indicators on
the Map
Problem
You want to display traffic as well as the little compass and scale indicators on the
map view.

350

|

Chapter 12: Maps and Location

Solution
Set the following properties of your map view to true:
• showsCompass
• showsTraffic
• showsScale

Discussion
Place a map view on your view and set the appropriate constraints on it so that it
stretches across the width and height of your view controller’s view. This is really
optional, but useful so the user can see the map view properly on all devices. Then
follow the steps outlined in Recipe 12.4 to place an annotation on the map. Write
code similar to the following in a method such as viewDidLoad:
map.showsCompass = true
map.showsTraffic = true
map.showsScale = true

The results will be similar to those shown in Figure 12-5. The scale is shown on the
top left and the compass on the top right. You have to rotate the map for the compass
to appear.

12.6 Displaying Traffic, Scale, and Compass Indicators on the Map

|

351