EKLocationReminders/EKLocationReminders/MapViewController.m
/* |
Copyright (C) 2015 Apple Inc. All Rights Reserved. |
See LICENSE.txt for this sample’s licensing information |
Abstract: |
This view controller displays a map with annotations if the app has access to Reminders and an empty map, otherwise. |
Tap any annotation's callout to create a reminder for that location. |
*/ |
#import "MyAnnotation.h" |
#import "EKRSConstants.h" |
#import "EKRSHelperClass.h" |
#import "MapViewController.h" |
#import "AddLocationReminder.h" |
#import "LocationReminderStore.h" |
#import "LocationTabBarController.h" |
const double EKLRRegionDelta = 2.12; |
const double EKLRRegionLatitude = 37.78699; |
const double EKLRRegionLongitude = -122.4401; |
static NSString *const EKRSAnnotationAddress = @"address"; |
static NSString *const EKRSAnnotationLatitude = @"latitude"; |
static NSString *const EKRSAnnotationLongitude = @"longitude"; |
static NSString *EKLRLocationsList = @"Locations"; |
static NSString *EKLRLocationsListExtension = @"plist"; |
static NSString * const kPinAnnotationViewIdentifier = @"pinAnnotationViewIdentifier"; |
@interface MapViewController () <CLLocationManagerDelegate> |
@property (nonatomic)id<MKAnnotation> selectedAnnotation; |
@property (weak, nonatomic) IBOutlet MKMapView *mapView; |
@property (nonatomic, strong) CLLocationManager *locationManager; |
@property (nonatomic, strong) EKStructuredLocation *selectedStructureLocation; |
@property (nonatomic, copy) NSString *currentUserLocationAddress; |
@end |
@implementation MapViewController |
-(id)initWithCoder:(NSCoder *)aDecoder |
{ |
self = [super initWithCoder:aDecoder]; |
if (self) |
{ |
[[NSNotificationCenter defaultCenter] addObserver:self |
selector:@selector(handleLTBControllerNotification:) |
name:LTBAccessGrantedNotification |
object:nil]; |
} |
return self; |
} |
#pragma mark - Location Access Methods |
- (void)checkLocationServicesAuthorizationStatus |
{ |
CLAuthorizationStatus status = [CLLocationManager authorizationStatus]; |
switch (status) |
{ |
case kCLAuthorizationStatusAuthorizedWhenInUse: |
[self accessGrantedForLocationServices]; |
break; |
case kCLAuthorizationStatusNotDetermined : |
[self requestLocationServicesAuthorization]; |
break; |
case kCLAuthorizationStatusDenied: |
case kCLAuthorizationStatusRestricted: |
{ |
if (self.mapView.annotations > 0) |
{ |
[self.mapView removeAnnotations:[NSArray arrayWithArray:self.mapView.annotations]]; |
} |
UIAlertController *alert = [EKRSHelperClass alertWithTitle:NSLocalizedString(@"Privacy Warning", nil) |
message:NSLocalizedString(@"Access was not granted for Location Services.", nil)]; |
[self presentViewController:alert animated:YES completion:nil]; |
} |
break; |
default: |
break; |
} |
} |
-(void)requestLocationServicesAuthorization |
{ |
if (self.locationManager == nil) |
{ |
self.locationManager = [[CLLocationManager alloc] init]; |
self.locationManager.delegate = self; |
} |
// Ask for user permission to find our location |
[self.locationManager requestWhenInUseAuthorization]; |
} |
#pragma mark - Handle LocationTabBarController Notification |
-(void)handleLTBControllerNotification:(NSNotification *)notification |
{ |
[self accessGrantedForReminders]; |
} |
#pragma mark - Handle Location Services Access |
/* |
This sample uses data from the Locations.plist file to create annotations for the map. Locations.plist includes an array of dictionaries |
that each represents the title, latitude, longitude, and address information of an annotation. Additionally, accessGrantedForLocationServices |
adds the current user location to Map. Update this file with data formatted as described above if you wish to test reminders around other locations. |
Note that you can obtain latitude, longitude, and delta information by following these steps: |
1) Implement |
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated |
2) Zoom or pan to the area you want in Map, then set a breakpoint there to obtain information about the region. |
3) Display the latitude, longitude, and delta information by executing po mapview.region in the debugger. |
*/ |
-(void)accessGrantedForLocationServices |
{ |
if (self.mapView.annotations > 0) |
{ |
[self.mapView removeAnnotations:[NSArray arrayWithArray:self.mapView.annotations]]; |
} |
// Locations.plist contains all data required for configuring the map's region and points of interest |
NSURL *plistURL = [[NSBundle mainBundle] URLForResource:EKLRLocationsList withExtension:EKLRLocationsListExtension]; |
NSArray *data = [NSArray arrayWithContentsOfURL:plistURL]; |
[self.mapView addAnnotations:[self fetchAnnotations:data]]; |
self.mapView.showsUserLocation = YES; |
} |
#pragma mark - Handle Reminders Access |
-(void)accessGrantedForReminders |
{ |
[self checkLocationServicesAuthorizationStatus]; |
} |
#pragma mark - Fetch Interest Points |
-(NSMutableArray *)fetchAnnotations:(NSArray *)locations |
{ |
NSMutableArray *annotations = [[NSMutableArray alloc] initWithCapacity:locations.count]; |
for (NSDictionary *dict in locations) |
{ |
MyAnnotation *myAnnotation = [[MyAnnotation alloc]initWithTitle:dict[EKRSTitle] |
latitude:[dict[EKRSAnnotationLatitude] doubleValue] |
longitude:[dict[EKRSAnnotationLongitude] doubleValue] |
address:dict[EKRSAnnotationAddress]]; |
[annotations addObject:myAnnotation]; |
} |
return annotations; |
} |
#pragma mark - CLLocationManagerDelegate |
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error |
{ |
NSLog(@"Error: %@",error.description); |
} |
// Called when the authorization status changes for Location Services |
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status |
{ |
// Check the authorization status and take the appropriate action |
[self checkLocationServicesAuthorizationStatus]; |
} |
#pragma mark - MKMapViewDelegate |
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation |
{ |
CLLocation *location = [[CLLocation alloc] initWithLatitude: userLocation.coordinate.latitude longitude:userLocation.coordinate.longitude]; |
CLGeocoder *geocoder = [[CLGeocoder alloc] init]; |
// Reverse-geocode the current user coordinates |
[geocoder reverseGeocodeLocation:location completionHandler:^(NSArray *placemarks, NSError *error) { |
if ((placemarks != nil) && (placemarks.count > 0)) |
{ |
CLPlacemark *placemark = placemarks.firstObject; |
self.currentUserLocationAddress = [NSString stringWithFormat:@"%@ %@ %@",placemark.subThoroughfare, placemark.thoroughfare,placemark.locality]; |
} |
}]; |
// Create a region using the current user location |
MKCoordinateRegion region; |
region.span = MKCoordinateSpanMake(EKLRRegionDelta, EKLRRegionDelta); |
region.center = CLLocationCoordinate2DMake(userLocation.location.coordinate.latitude, userLocation.location.coordinate.longitude); |
[self.mapView setRegion:region animated:YES]; |
} |
- (MKAnnotationView *)mapView:(MKMapView *)theMapView viewForAnnotation:(id <MKAnnotation>)annotation |
{ |
MKPinAnnotationView *pinView =(MKPinAnnotationView *) [self.mapView dequeueReusableAnnotationViewWithIdentifier:kPinAnnotationViewIdentifier]; |
if (!pinView) |
{ |
pinView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation |
reuseIdentifier:kPinAnnotationViewIdentifier]; |
if ([pinView respondsToSelector:@selector(pinTintColor)]) |
{ |
((MKPinAnnotationView *)pinView).pinTintColor = [MKPinAnnotationView purplePinColor]; |
} |
else |
{ |
// ignore this compile warning, since we already have implemented the replacement above |
#pragma GCC diagnostic push |
#pragma GCC diagnostic ignored "-Wdeprecated-declarations" |
((MKPinAnnotationView *)pinView).pinColor = MKPinAnnotationColorPurple; |
#pragma GCC diagnostic pop |
} |
pinView.animatesDrop = YES; |
pinView.canShowCallout = YES; |
pinView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure]; |
} |
else |
{ |
pinView.annotation = annotation; |
} |
return pinView; |
} |
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control |
{ |
UIStoryboard *story = [UIStoryboard storyboardWithName:@"LocationReminders" bundle:nil]; |
UINavigationController *navigationController = (UINavigationController *)[story instantiateViewControllerWithIdentifier:@"navAddLocationReminderVCID"]; |
AddLocationReminder *addLocationReminderViewController = (AddLocationReminder *)navigationController.topViewController; |
if ([view.annotation isKindOfClass:[MyAnnotation class]]) |
{ |
MyAnnotation *myAnnotation = (MyAnnotation *)view.annotation; |
addLocationReminderViewController.name = myAnnotation.title; |
addLocationReminderViewController.address = myAnnotation.address; |
} |
else |
{ |
MKUserLocation *userLocation = view.annotation; |
// We selected the user location |
addLocationReminderViewController.name = userLocation.title; |
addLocationReminderViewController.address = self.currentUserLocationAddress; |
} |
self.selectedAnnotation = view.annotation; |
[self.navigationController presentViewController:navigationController animated:YES completion:nil]; |
} |
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated |
{ |
} |
#pragma mark - Unwind Segues |
// Dismiss the Add Location Reminder view controller |
- (IBAction)cancel:(UIStoryboardSegue*)sender |
{ |
} |
- (IBAction)done:(UIStoryboardSegue*)sender |
{ |
AddLocationReminder *addLocationReminderViewController = (AddLocationReminder *)sender.sourceViewController; |
NSDictionary *dictionary = addLocationReminderViewController.userInput; |
// If the selected annotation is the current user location, show its address rather than Current Location. Show its title, otherwise. |
EKStructuredLocation *location = ([self.selectedAnnotation isKindOfClass:[MKUserLocation class]]) ? [EKStructuredLocation locationWithTitle:self.currentUserLocationAddress] :[EKStructuredLocation locationWithTitle:self.selectedAnnotation.title]; |
location.geoLocation = [[CLLocation alloc] initWithLatitude:self.selectedAnnotation.coordinate.latitude |
longitude:self.selectedAnnotation.coordinate.longitude]; |
// Convert from miles to meters before assigning it to the radius property |
location.radius = kMeter *[dictionary[EKRSLocationRadius] doubleValue]; |
LocationReminder *newLocationReminder = [[LocationReminder alloc] initWithTitle:dictionary[EKRSTitle] |
proximity:dictionary[EKRSLocationProximity] |
structureLocation:location]; |
[[LocationReminderStore sharedInstance] createLocationReminder:newLocationReminder]; |
} |
#pragma mark - Memory Management |
- (void)didReceiveMemoryWarning |
{ |
[super didReceiveMemoryWarning]; |
} |
- (void)dealloc |
{ |
[[NSNotificationCenter defaultCenter] removeObserver:self |
name:LTBAccessGrantedNotification |
object:nil]; |
} |
@end |
Copyright © 2015 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2015-11-13