Finding Business Listings and Displaying with MapKit – Part 2

Here we go with Part 2 of “Finding Business Listings and Displaying with MapKit”. In the first part we looked at how to gather the data from Google Maps. Now, we are going to take that data and display it using the MapKit API. We are also going to find our map’s center point using CoreLocation. Here’s an overview what is going to happen:

  1. Create an instance of the LocationsMap
  2. Call the findLocationsByKeyword method of LocationsMap instance.
  3. Inside the LocationsMap class, a series of events begin, starting with creating an instance of CLLocationManager and calling startUpdatingLocation on that instance.
  4. Once the user’s location has been determined, we retrieve the data from MapDataParser which we discussed at length in part one.
  5. With that data we loop through and create extended versions of MKAnnotation to store the data and add them to the MKMapView.
  6. Finally, we specify the MKPinAnnotionView to use for each annoation we added to the map.

As in part 1, you will need to add the CoreLocation and MapKit frameworks to you project. The workhorse of part 2 is the LocationsMap class. Let’s take a look at the interface.

#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#import "MapDataParser.h"

 

@interface LocationsMap : UIView <MKMapViewDelegate, CLLocationManagerDelegate>
{
    MKMapView *mapView;
    CLLocationManager *locationManager;
    MapDataParser *mapDataParser;
    NSMutableArray *currentAnnotations;
    CLLocation *currentLocation;
    NSString *searchKeyword;
}

@property (nonatomic, retain) MKMapView *mapView;
@property (nonatomic, retain) CLLocation *currentLocation;
@property (nonatomic, retain) NSString *searchKeyword;
@property (nonatomic, retain) NSMutableArray *currentAnnotations;

-(void) findLocationsByKeyword:(NSString *)searchKeyword;

@end

You can see we need to implement the MKMapViewDelegate and the CLLocationManagerDelegate protocols. Even though there is only one public method, as seen in the overview, this triggers a chain of events. Here’s the implementation:

#import "LocationsMap.h"
#import "MapDataParser.h"
#import "MapLocationVO.h"
#import "BLAnnotation.h"

 

@implementation LocationsMap

@synthesize mapView, currentLocation, searchKeyword, currentAnnotations;


- (id)initWithFrame:(CGRect)frame
{
    if ((self = [super initWithFrame:frame]))
    {
        // Create the map view
       
        mapView = [[MKMapView alloc] initWithFrame:CGRectMake(0, 0, frame.size.width, frame.size.height)];
        mapView.delegate = self;
        mapView.showsUserLocation = YES;
        [self addSubview:mapView];
       
        // array to store annotations added to the map
        currentAnnotations = [[NSMutableArray alloc] init];
    }
   
    return self;
}

-(void) findLocationsByKeyword:(NSString *)keyword
{
    // if we haven't gotten the user's location yet, create an instance of CLLocationManager to find it.
    // Otherwise, go ahead and call "getBusinessListingsByKeyword" on our MapDataParser instance
   
    if(!locationManager)
    {
        locationManager = [[CLLocationManager alloc] init];
        locationManager.desiredAccuracy=kCLLocationAccuracyNearestTenMeters;
        locationManager.delegate = self;
        [locationManager startUpdatingLocation];
       
        // listen for map data parsing completion
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(displayBusinessLocations:) name:@"mapDataParseComplete" object:nil];
    }
    else
    {
        [mapDataParser getBusinessListingsByKeyword:keyword atLat:currentLocation.coordinate.latitude atLng:currentLocation.coordinate.longitude];
    }
   
    // save the keyword state
    searchKeyword = keyword;
}

- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{  
    // User location has been found/updated, load map data now.
   
    locationManager.delegate = nil;
    [locationManager stopUpdatingLocation];
   
    if(!mapDataParser) mapDataParser = [[MapDataParser alloc] init];
   
    currentLocation = [newLocation copy];
   
    [mapDataParser getBusinessListingsByKeyword:searchKeyword atLat:currentLocation.coordinate.latitude atLng:currentLocation.coordinate.longitude];
}

- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
    // Failed to find the user's location. This error occurs when the user declines the location request
    // or has location servives turned off.
   
    NSString * errorString = @"Unable to determine your current location.";
    UIAlertView * errorAlert = [[UIAlertView alloc] initWithTitle:@"Error Locating" message:errorString delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
    [errorAlert show];
    [locationManager stopUpdatingLocation];
}

-(void) displayBusinessLocations:(id)sender
{  
    // User location has been found and map data is loaded.
    // Now create the annotations and add them to the map.
   
    MKCoordinateRegion region;
    MKCoordinateSpan span;
    span.latitudeDelta = 0.2;
    span.longitudeDelta = 0.2;
   
    region.span=span;
    region.center=currentLocation.coordinate;
   
    [mapView setRegion:region animated:NO];
    [mapView regionThatFits:region];
   
    for(MapLocationVO *mapLocationVO in mapDataParser.locationData)
    {
        // Create annotations for the returned google maps data.
       
        BLAnnotation *businessListingAnnotation = [[BLAnnotation alloc] initWithCoordinate: mapLocationVO.mapLocation.coordinate title: mapLocationVO.locationTitle subtitle:mapLocationVO.locationSnippet];
       
        [mapView addAnnotation: businessListingAnnotation];
        [currentAnnotations addObject: businessListingAnnotation];
       
        [businessListingAnnotation release];
        businessListingAnnotation = nil;
    }
}

- (MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>) annotation
{
    // Specify which MKPinAnnotationView to use for each annotation.
   
    MKPinAnnotationView *businessListingPin = (MKPinAnnotationView *)[self.mapView dequeueReusableAnnotationViewWithIdentifier: @"BLPin" ];

    if (businessListingPin == nil)
    {
        businessListingPin = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@"BLPin"];
    }
    else
    {
        businessListingPin.annotation = annotation;
    }
   
    // If the annotation is the user's location, don't show the callout and change the pin color
    if(annotation == self.mapView.userLocation)
    {
        businessListingPin.canShowCallout = NO;
        businessListingPin.pinColor = MKPinAnnotationColorRed;
    }
    else
    {
        businessListingPin.canShowCallout = YES;
        businessListingPin.pinColor = MKPinAnnotationColorPurple;
    }
   
    return businessListingPin;
}

- (void)dealloc {
    [super dealloc];
   
    [mapView release]; mapView = nil;
    [locationManager release]; locationManager = nil;
    [mapDataParser release]; mapDataParser = nil;
    [currentAnnotations release]; currentAnnotations = nil;
    [currentLocation release]; currentLocation = nil;
    [searchKeyword release]; searchKeyword = nil;
}

@end

After the CLLocationManager is created and we delegate it to LocationsMap, it will fire either the didUpdateToLocation or the didFailWithError. The didFailWithError delegate method will be fired if the user declines the location services request or they have location services turned off. Otherwise, didUpdateToLocation will fire notifying us that the the location has been found.

At that point, we stop updating the location and set the delegate for CLLocationManager to nil becuase we no longer need to check the location. Next we call the getBusinessListingsByKeyword method of the MapDataParser we discussed in part 1 and listen for the mapDataParseComplete event to fire.

Next, the displayBusinessLocations handler will be called (barring any issues retrieving the data). At this point we create our custom version of the MKAnnotation for each MapLocationVO and add those to out map view.

At this point, our data is technically integrated within the map view. We just need to specify which view to use to represent the data. We do this in the viewForAnnotation delegate method. We are going to use the basic MKPinAnnotationView. Like a table cell, we try to reuse the pins or create a new one if needed. Finally, we check if the current annotation is the user’s location pin and if so we don’t show an annotation and change the pin color.

Now we have our locations represented on the map, we are almost done. You’ll notice longer subtitles get cut off in the built in annotation views. This is why we’ll need to create our own custom annotation views to display the address and phone number better. We’ll also add some other finishing touches in part 3. In the meantime, here’s the source. Enjoy!

SOURCE FILES

http://www.zen-sign.com/finding-business-listings-and-displaying-with-mapkit-part-2/

posted @ 2010-10-22 14:59  周宏伟  阅读(314)  评论(0编辑  收藏  举报