I have a search setup that displays the response of a MKLocalSearchRequest in UITableVIew
I want to clean up all the responses from the response of each search. Here is my shot at accomplishing this so far, inspired by this post Multiple Locations on Map (using MKMapItem and CLGeocoder)
Here is my code.
@interface ViewController () <UISearchBarDelegate,UISearchDisplayDelegate,UITextFieldDelegate>
@property (nonatomic, strong) UISearchDisplayController *searchController;
@property (nonatomic, strong) UISearchBar *searchBar;
@property (nonatomic, strong) MKLocalSearch *localSearch;
@property (nonatomic, strong) MKLocalSearchResponse *localSearchResponse;
@property (nonatomic, strong) NSArray *dirtyResponseArray;
@property (nonatomic, strong) NSMutableArray *geocodedResultsArray;
@end
@implementation ViewController
-(void)startSearch:(NSString *)searchString
{
if (self.localSearch.searching)
[self.localSearch cancel];
MKLocalSearchRequest *request = [[MKLocalSearchRequest alloc] init];
request.naturalLanguageQuery = searchString;
request.region = MKCoordinateRegionMake(self.currentLocation.coordinate, self.mapView.region.span);
MKLocalSearchCompletionHandler completionHandler = ^(MKLocalSearchResponse *response, NSError *error){
if (error != nil) return;
else {
self.dirtyResponseArray = response.mapItems;
[self operation];
[self.searchController.searchResultsTableView reloadData];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}
[self.searchDisplayController.searchResultsTableView reloadData];
};
if (self.localSearch != nil)
self.localSearch = nil;
self.localSearch = [[MKLocalSearch alloc] initWithRequest:request];
[self.localSearch startWithCompletionHandler:completionHandler];
}
-(void)operation
{
CLGeocoder *geocoder = [[CLGeocoder alloc]init];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSOperation *finalCompletionOperation = [NSBlockOperation blockOperationWithBlock:^{
[MKMapItem openMapsWithItems:self.geocodedResultsArray launchOptions:nil];
NSLog(@"Local Search Response To use in tableview =================== %@", self.geocodedResultsArray);
}];
NSOperation *previousCompletionHandler = nil;
//Issue is probably right here. How should I handle the MKMapItem in the array?
//NSString *address = [self.dirtyResponseArray valueForKey:@"address"][@"formattedAddress"]; ??
for (NSString *address in self.dirtyResponseArray) {
NSBlockOperation *geocodeRequest = [[NSBlockOperation alloc] init];
if (previousCompletionHandler) [geocodeRequest addDependency:previousCompletionHandler];
NSBlockOperation *geocodeCompletionHandler = [[NSBlockOperation alloc] init];
[finalCompletionOperation addDependency:geocodeCompletionHandler];
[geocodeRequest addExecutionBlock:^{ [geocoder geocodeAddressString:address
completionHandler:^(NSArray *placemarks, NSError *error)
{
[geocodeCompletionHandler addExecutionBlock:^{
if (error) NSLog(@"%@", error);
else if ([placemarks count] > 0)
{
CLPlacemark *geocodedPlacemark = [placemarks objectAtIndex:0];
MKPlacemark *placemark = [[MKPlacemark alloc] initWithCoordinate:geocodedPlacemark.location.coordinate
addressDictionary:geocodedPlacemark.addressDictionary];
MKMapItem *mapItem = [[MKMapItem alloc] initWithPlacemark:placemark];
[mapItem setName:geocodedPlacemark.name];
[self.geocodedResultsArray addObject:mapItem];
}
}];
[queue addOperation:geocodeCompletionHandler];
}];
}];
[queue addOperation:geocodeRequest];
previousCompletionHandler = geocodeCompletionHandler;
}
[queue addOperation:finalCompletionOperation];
}
@end
I'm not sure how to handle each MKMapItem. Right now its throwing this error
-[MKMapItem length]: unrecognized selector sent to instance 0x7f813c9c6200
The
CLGeocoder
method,geocodeAddressString
, which is referenced in that other question, is designed for geocoding an address.But you're performing a
MKLocalSearch
within a particular region, which returnsMKMapItem
objects which are already geocoded. As a result, you can entirely remove thegeocodeAddressString
code from your code sample.In fact, this explains the source of your exception, that you're taking the
MKMapItem
returned byMKLocalSearch
, and trying to use it as the search string parameter ofCLGeocoder
methodgeocodeAddressString
(which is only used for looking up string representations of addresses).Bottom line, to do a local search and show the results in the Maps app is far easier than what you've contemplated above. All you need to do is:
Or, if you wanted to show it on your own
MKMapView
, you could do something likeIn practice, in this latter example where you're showing results on your own map view, you might create your own custom annotation class so that you have greater control over your rendering of the annotation view in
viewForAnnotation
method. But hopefully this illustrates the main observation, that when usingMKLocalSearch
, you should not be usingCLGeocoder
methodgeocodeAddressString
at all.