My solution for Reverse Geocoding challenge


#1

I don’t know about anyone else but I thought this challenge was TOUGH. I had a hard time following the documentation for it.

Since MKReverseGeocoder is deprecated I used CLGeocoder instead. Pretty much everything happens in the MapPoint.m file.

Here is my MapPoint.h file:

#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>
#import <MapKit/MapKit.h>

@interface MapPoint : NSObject <MKAnnotation>
{
    NSString *title;
    NSString *subtitle;
    NSString *locality; //City
    NSString *administrativeArea; //State
    
    CLGeocoder *geoCoder;
    
    CLLocationCoordinate2D coordinate;
}

//A new designated initializer for instances of MapPoint
-(id)initWithCoordinate:(CLLocationCoordinate2D)c title:(NSString *)t location: (CLLocation *)l;

//This is a required property from MKAnnotation
@property(nonatomic, readonly) CLLocationCoordinate2D coordinate;

//This is an optional property from MKAnnotation
@property(nonatomic, copy) NSString *title, *subtitle, *locality, *administrativeArea;

@end

Here is my MapPoint.m file:

//
//  MapPoint.m
//  Whereami
//
//  Created by Scott Nelson on 2/6/12.
//  Copyright (c) 2012 NA. All rights reserved.
//

#import "MapPoint.h"

@implementation MapPoint

@synthesize coordinate, title, subtitle, locality, administrativeArea;

-(id)initWithCoordinate:(CLLocationCoordinate2D)c title:(NSString *)t location:(CLLocation *)l
{
    self = [super init];
    
    if(self)
    {
        //Set properties
        coordinate = c;
        [self setTitle:t];
        
        //Create reverseGeocode
        if(!geoCoder)
        {
            geoCoder = [[CLGeocoder alloc] init];
        }
        
        [geoCoder reverseGeocodeLocation:l
                       completionHandler:^(NSArray *placemarks, NSError *error) {
                           
                           //Create date 
                           NSString *locationStamp = [NSDateFormatter localizedStringFromDate: [NSDate date]
                                                                                    dateStyle:NSDateFormatterShortStyle 
                                                                                    timeStyle:NSDateFormatterShortStyle];
                           
                           //Loop through the placemarks
                           for(CLPlacemark *p in placemarks)
                           {
                               [self setLocality:[p locality]];
                               [self setAdministrativeArea:[p administrativeArea]];
                               
                           }
                           
                           //Create the location stamp format
                           locationStamp = [locationStamp stringByAppendingString:@" "];
                           locationStamp = [locationStamp stringByAppendingString:[self locality]];
                           locationStamp = [locationStamp stringByAppendingString:@" "];
                           locationStamp = [locationStamp stringByAppendingString:[self administrativeArea]];
                           
                           //Assign location stamp
                           [self setSubtitle:locationStamp];
                       }];
    }
    
    return self;
}

-(void)dealloc
{
    [geoCoder release];
    [super dealloc];
}
@end

Here is my Wheremidelegate.h file:

#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#import <MapKit/MapKit.h>
#import "MapPoint.h"

@interface WhereamiAppDelegate : UIResponder 
<UIApplicationDelegate, CLLocationManagerDelegate, MKMapViewDelegate, UITextFieldDelegate>
{
    CLLocationManager *locationManager;
    
    IBOutlet MKMapView *worldView;
    IBOutlet UIActivityIndicatorView *activityIndicator;
    IBOutlet UITextField *locationTitleField;
    
}

@property (strong, nonatomic) IBOutlet UIWindow *window;

-(void)findLocation;
-(void)foundLocation:(CLLocation *)loc;

@end

and here is my WhereamiAppDelegate.m file:

//
//  WhereamiAppDelegate.m
//  Whereami
//
//  Created by Scott Nelson on 2/6/12.
//  Copyright (c) 2012 NA. All rights reserved.
//

#import "WhereamiAppDelegate.h"

@implementation WhereamiAppDelegate

@synthesize window = _window;

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    //Create location manager object
    locationManager = [[CLLocationManager alloc] init];
    
    //Set the delegate of the location manager
    [locationManager setDelegate:self];
    
    //We want all results from the location manager
    [locationManager setDistanceFilter:kCLDistanceFilterNone];
    [locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
    
    //Show users location on the map
    [worldView setShowsUserLocation:YES];

    [[self window] makeKeyAndVisible];
    return YES;
}

- (void)locationManager:(CLLocationManager *)manager 
    didUpdateToLocation:(CLLocation *)newLocation 
           fromLocation:(CLLocation *)oldLocation
{
    NSLog(@"%@", newLocation);
    
    //How many seconds ago was this new location created?
    NSTimeInterval t = [[newLocation timestamp] timeIntervalSinceNow];
    
    //CLLocationManagers will return the last found location of the
    //device first, you don't want that data in this case.
    //If this location was made more than 3 minutes ago, ignore it.
    if(t < -180)
    {
        //This is cached data, you don't want it, keep looking
        return;
    }
    
    [self foundLocation:newLocation];
}

- (void)locationManager:(CLLocationManager *)manager 
       didFailWithError:(NSError *)error
{
    NSLog(@"Could not find location: %@", error);
}

//Used for zooming into Map
- (void)mapView:(MKMapView *)mapView 
    didUpdateUserLocation:(MKUserLocation *)userLocation
{
    CLLocationCoordinate2D loc = [userLocation coordinate];
    MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(loc, 250, 250);
    [worldView setRegion:region animated:YES];
}

//Used for entering text
-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
    [self findLocation];
    
    [textField resignFirstResponder];
    
    return YES;
}

-(void)findLocation
{
    [locationManager startUpdatingLocation];
    [activityIndicator startAnimating];
    [locationTitleField setHidden:YES];
}

-(void)foundLocation:(CLLocation *)loc
{
    CLLocationCoordinate2D coord = [loc coordinate];
    
    //Create an instance of MapPoint with the current data
    MapPoint *mp = [[MapPoint alloc] initWithCoordinate:coord 
                                                  title:[locationTitleField text]
                                               location:loc];
                    
    //Add it to the map view
    [worldView addAnnotation:mp];
    
    
    //MKMapview retains its annotations, we can release
    [mp release];
    
    //Zoom the region to this location
    MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(coord, 250, 250);
    [worldView setRegion:region animated:YES];
    
    [locationTitleField setText:@""];
    [activityIndicator stopAnimating];
    [locationTitleField setHidden:NO];
    [locationManager stopUpdatingLocation];
}



- (void)applicationWillResignActive:(UIApplication *)application
{
    /*
     Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
     Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
     */
}

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    /*
     Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 
     If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
     */
}

- (void)applicationWillEnterForeground:(UIApplication *)application
{
    /*
     Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
     */
}

- (void)applicationDidBecomeActive:(UIApplication *)application
{
    /*
     Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
     */
}

- (void)applicationWillTerminate:(UIApplication *)application
{
    /*
     Called when the application is about to terminate.
     Save data if appropriate.
     See also applicationDidEnterBackground:.
     */
}

@end

#2

Yet another solution, all contained in MapPoint.m:

[code]-(id)initWithCoordinate:(CLLocationCoordinate2D)c title:(NSString *)t
{
self = [super init];

if (self)
{
    coordinate = c;
    
    [self setTitle:t];

    CLLocation  *location;
    location = [[CLLocation alloc] initWithLatitude: coordinate.latitude longitude: coordinate.longitude];
    
    CLGeocoder  *geocoder;
    geocoder = [[CLGeocoder alloc] init];
    
    [geocoder reverseGeocodeLocation:location completionHandler:^(NSArray *placemarks, NSError *error)
    {
        if (error)
        {
            NSLog(@"A Geocode error occurred: %@", error);
            return;
        }
     
        if( placemarks && placemarks.count > 0 )
        {
            CLPlacemark *p = [placemarks objectAtIndex: 0];
            
            NSString *locationString;
            locationString = [NSString stringWithFormat: @"%@ %@, %@, %@",
                                    [p subThoroughfare], [p thoroughfare], [p locality], [p administrativeArea]];

            [self setSubtitle: locationString];
        }
    }];
    
     [location release];
    [geocoder release];
}

return self;

}
[/code]