/* |
File: MyTableViewController.m |
Abstract: Primary view controller used to display search results. |
Version: 1.0 |
|
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple |
Inc. ("Apple") in consideration of your agreement to the following |
terms, and your use, installation, modification or redistribution of |
this Apple software constitutes acceptance of these terms. If you do |
not agree with these terms, please do not use, install, modify or |
redistribute this Apple software. |
|
In consideration of your agreement to abide by the following terms, and |
subject to these terms, Apple grants you a personal, non-exclusive |
license, under Apple's copyrights in this original Apple software (the |
"Apple Software"), to use, reproduce, modify and redistribute the Apple |
Software, with or without modifications, in source and/or binary forms; |
provided that if you redistribute the Apple Software in its entirety and |
without modifications, you must retain this notice and the following |
text and disclaimers in all such redistributions of the Apple Software. |
Neither the name, trademarks, service marks or logos of Apple Inc. may |
be used to endorse or promote products derived from the Apple Software |
without specific prior written permission from Apple. Except as |
expressly stated in this notice, no other rights or licenses, express or |
implied, are granted by Apple herein, including but not limited to any |
patent rights that may be infringed by your derivative works or by other |
works in which the Apple Software may be incorporated. |
|
The Apple Software is provided by Apple on an "AS IS" basis. APPLE |
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION |
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS |
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND |
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. |
|
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL |
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, |
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED |
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), |
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE |
POSSIBILITY OF SUCH DAMAGE. |
|
Copyright (C) 2013 Apple Inc. All Rights Reserved. |
|
*/ |
|
#import "MyTableViewController.h" |
#import "MapViewController.h" |
|
#import <MapKit/MapKit.h> |
|
// note: we use a custom segue here in order to cache/reuse the |
// destination view controller (i.e. MapViewController) each time you select a place |
// |
@interface DetailSegue : UIStoryboardSegue |
@end |
|
@implementation DetailSegue |
|
- (void)perform |
{ |
// our custom segue is being fired, push the map view controller |
MyTableViewController *sourceViewController = self.sourceViewController; |
MapViewController *destinationViewController = self.destinationViewController; |
[sourceViewController.navigationController pushViewController:destinationViewController animated:YES]; |
} |
|
@end |
|
|
#pragma mark - |
|
static NSString *kCellIdentifier = @"cellIdentifier"; |
|
@interface MyTableViewController () |
|
@property (nonatomic, assign) MKCoordinateRegion boundingRegion; |
|
@property (nonatomic, strong) MKLocalSearch *localSearch; |
@property (nonatomic, weak) IBOutlet UIBarButtonItem *viewAllButton; |
@property (nonatomic, strong) CLLocationManager *locationManager; |
@property (nonatomic) CLLocationCoordinate2D userLocation; |
|
@property (nonatomic, strong) DetailSegue *detailSegue; |
@property (nonatomic, strong) DetailSegue *showAllSegue; |
@property (nonatomic, strong) MapViewController *mapViewController; |
|
- (IBAction)showAll:(id)sender; |
|
@end |
|
|
#pragma mark - |
|
@implementation MyTableViewController |
|
- (void)viewDidLoad |
{ |
[super viewDidLoad]; |
|
// start by locating user's current position |
self.locationManager = [[CLLocationManager alloc] init]; |
self.locationManager.delegate = self; |
[self.locationManager startUpdatingLocation]; |
|
// create and reuse for later the mapViewController |
self.mapViewController = [[self storyboard] instantiateViewControllerWithIdentifier:@"MapViewControllerID"]; |
|
// use our custom segues to the destination view controller is reused |
self.detailSegue = [[DetailSegue alloc] initWithIdentifier:@"showDetail" |
source:self |
destination:self.mapViewController]; |
|
self.showAllSegue = [[DetailSegue alloc] initWithIdentifier:@"showAll" |
source:self |
destination:self.mapViewController]; |
} |
|
- (void)viewWillAppear:(BOOL)animated |
{ |
self.navigationController.navigationBar.barStyle = UIBarStyleBlack; |
[super viewWillAppear:animated]; |
} |
|
- (BOOL)shouldAutorotate |
{ |
return YES; |
} |
|
- (NSUInteger)supportedInterfaceOrientations |
{ |
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) |
return UIInterfaceOrientationMaskAll; |
else |
return UIInterfaceOrientationMaskAllButUpsideDown; |
} |
|
|
#pragma mark - UITableView delegate methods |
|
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section |
{ |
return [self.places count]; |
} |
|
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath |
{ |
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellIdentifier forIndexPath:indexPath]; |
|
MKMapItem *mapItem = [self.places objectAtIndex:indexPath.row]; |
cell.textLabel.text = mapItem.name; |
|
return cell; |
} |
|
- (IBAction)showAll:(id)sender |
{ |
// pass the new bounding region to the map destination view controller |
self.mapViewController.boundingRegion = self.boundingRegion; |
|
// pass the places list to the map destination view controller |
self.mapViewController.mapItemList = self.places; |
|
[self.showAllSegue perform]; |
} |
|
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath |
{ |
// pass the new bounding region to the map destination view controller |
self.mapViewController.boundingRegion = self.boundingRegion; |
|
// pass the individual place to our map destination view controller |
NSIndexPath *selectedItem = [self.tableView indexPathForSelectedRow]; |
self.mapViewController.mapItemList = [NSArray arrayWithObject:[self.places objectAtIndex:selectedItem.row]]; |
|
[self.detailSegue perform]; |
} |
|
|
#pragma mark - UISearchBarDelegate |
|
- (void)searchBarCancelButtonClicked:(UISearchBar *) searchBar |
{ |
[searchBar resignFirstResponder]; |
} |
|
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar |
{ |
[searchBar setShowsCancelButton:YES animated:YES]; |
} |
|
- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar |
{ |
[searchBar setShowsCancelButton:NO animated:YES]; |
} |
|
- (void)startSearch:(NSString *)searchString |
{ |
if (self.localSearch.searching) |
{ |
[self.localSearch cancel]; |
} |
|
// confine the map search area to the user's current location |
MKCoordinateRegion newRegion; |
newRegion.center.latitude = self.userLocation.latitude; |
newRegion.center.longitude = self.userLocation.longitude; |
|
// setup the area spanned by the map region: |
// we use the delta values to indicate the desired zoom level of the map, |
// (smaller delta values corresponding to a higher zoom level) |
// |
newRegion.span.latitudeDelta = 0.112872; |
newRegion.span.longitudeDelta = 0.109863; |
|
MKLocalSearchRequest *request = [[MKLocalSearchRequest alloc] init]; |
|
request.naturalLanguageQuery = searchString; |
request.region = newRegion; |
|
MKLocalSearchCompletionHandler completionHandler = ^(MKLocalSearchResponse *response, NSError *error) |
{ |
if (error != nil) |
{ |
NSString *errorStr = [[error userInfo] valueForKey:NSLocalizedDescriptionKey]; |
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Could not find places" |
message:errorStr |
delegate:nil |
cancelButtonTitle:@"OK" |
otherButtonTitles:nil]; |
[alert show]; |
} |
else |
{ |
self.places = [response mapItems]; |
|
// used for later when setting the map's region in "prepareForSegue" |
self.boundingRegion = response.boundingRegion; |
|
self.viewAllButton.enabled = self.places != nil ? YES : NO; |
|
[self.tableView reloadData]; |
} |
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO; |
}; |
|
if (self.localSearch != nil) |
{ |
self.localSearch = nil; |
} |
self.localSearch = [[MKLocalSearch alloc] initWithRequest:request]; |
|
[self.localSearch startWithCompletionHandler:completionHandler]; |
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES; |
} |
|
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar |
{ |
[searchBar resignFirstResponder]; |
|
// check to see if Location Services is enabled, there are two state possibilities: |
// 1) disabled for entire device, 2) disabled just for this app |
// |
NSString *causeStr = nil; |
|
// check whether location services are enabled on the device |
if ([CLLocationManager locationServicesEnabled] == NO) |
{ |
causeStr = @"device"; |
} |
// check the application’s explicit authorization status: |
else if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusDenied) |
{ |
causeStr = @"app"; |
} |
else |
{ |
// we are good to go, start the search |
[self startSearch:searchBar.text]; |
} |
|
if (causeStr != nil) |
{ |
NSString *alertMessage = [NSString stringWithFormat:@"You currently have location services disabled for this %@. Please refer to \"Settings\" app to turn on Location Services.", causeStr]; |
|
UIAlertView *servicesDisabledAlert = [[UIAlertView alloc] initWithTitle:@"Location Services Disabled" |
message:alertMessage |
delegate:nil |
cancelButtonTitle:@"OK" |
otherButtonTitles:nil]; |
[servicesDisabledAlert show]; |
} |
} |
|
|
#pragma mark - CLLocationManagerDelegate methods |
|
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation |
{ |
// remember for later the user's current location |
self.userLocation = newLocation.coordinate; |
|
[manager stopUpdatingLocation]; // we only want one update |
|
manager.delegate = nil; // we might be called again here, even though we |
// called "stopUpdatingLocation", remove us as the delegate to be sure |
} |
|
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error |
{ |
// report any errors returned back from Location Services |
} |
|
@end |