Make the map go direct to the place I want by name - ios

I got 2 ViewControllers:
In the first I got TableView with name of countries.
When I click on one of the countries's cell it moves to the second ViewControl with performSegueWithIdentifier & saves the cell's name (for example: "France") in a variable outside the ViewController. in the seconds ViewController I got a MapKit View. I don't know how to make the map to go to "France".
Any suggestions?

- (void)performSearchWithLocationName:(NSString*)CountryName{
//#define METERS_PER_MILE 1.344
MKLocalSearchRequest *request =
[[MKLocalSearchRequest alloc] init];
request.naturalLanguageQuery = CountryName;
request.region = _mapView.region;
MKLocalSearch *search =
[[MKLocalSearch alloc]initWithRequest:request];
[search startWithCompletionHandler:^(MKLocalSearchResponse
*response, NSError *error) {
if (response.mapItems.count == 0){
NSLog(#"No Matches");
}
else{
for (MKMapItem *item in response.mapItems)
{
MKPointAnnotation *annotation =
[[MKPointAnnotation alloc]init];
annotation.coordinate = item.placemark.coordinate;
annotation.title = item.name;
NSLog(#"%#", annotation.title);
[_mapView addAnnotation:annotation];
MKCoordinateRegion region;
region = MKCoordinateRegionMakeWithDistance(annotation.coordinate,0.3*METERS_PER_MILE, 0.3*METERS_PER_MILE);
[self.mapView setRegion:[self.mapView regionThatFits:region] animated:YES];
}
}
}];
}
//You can implement map delegate
-(void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view{
NSLog(#"selected annotation and it's coordinates =>%f & %f",[view.annotation coordinate].latitude,[view.annotation coordinate].longitude);
}

Setp 1 : Getting latitude & longitude using location name
Setp 2 : Create the CLLocationCoordinate2D Using that latitude and longitude
Setp 3 : Create the MKCoordinateRegion
Setp 2 : Change the Mapview Region

Related

Mkpolyline is not being drawn

The route is clearly being made but the polyline is not drawn. I am able to find the total distance, and have verified that the coordinates we are using are not (0,0). Is there something wrong with the delegate, since it seems that both the addOverlay and addAnnotation (called in a custom method shown below called createAndAddAnnotationForCoordinate) methods are not working?
-(void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
//generates map view
mapView = [[MKMapView alloc]initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width,
self.view.bounds.size.height)];
//setting delegate to self is not fixing the error
mapView.delegate = self;
mapView.mapType = MKMapTypeStandard;
//converting CLLocationCoordinate2D to MKPlacemark
MKPlacemark *startPlacemark = [[MKPlacemark alloc]
initWithCoordinate: addressOneCoords addressDictionary:
[NSDictionary dictionaryWithObjectsAndKeys:nil]];
MKPlacemark *endPlacemark = [[MKPlacemark alloc]initWithCoordinate: addressTwoCoords addressDictionary:[NSDictionary dictionaryWithObjectsAndKeys:nil]];
//converting MKPlacemark to MKMapItem
MKMapItem *start = [[MKMapItem alloc ]initWithPlacemark:startPlacemark];
MKMapItem *end = [[MKMapItem alloc]initWithPlacemark:endPlacemark];
MKDirectionsRequest *request = [MKDirectionsRequest new];
[request setSource:start];
[request setDestination:end];
[request setTransportType:MKDirectionsTransportTypeAutomobile];
request.requestsAlternateRoutes = YES;
//Just to check if the coordinates were transferred successfully between view controllers and they did transfer successfully
NSLog(#"address one lat is %f",addressOneCoords.latitude);
NSLog(#"address one lon is %f",addressOneCoords.longitude);
NSLog(#"address two lat is %f",addressTwoCoords.latitude);
NSLog(#"address two lon is %f",addressTwoCoords.longitude);
MKDirections *directions = [[MKDirections alloc]initWithRequest:request];
[directions calculateDirectionsWithCompletionHandler:^(MKDirectionsResponse response, NSError error){
//if the route can't be created
if(error){
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Unable to create route" message:#"Go back to check if addresses are valid" delegate:nil cancelButtonTitle:#"Ok"otherButtonTitles:nil];
[alert show];
}
else{
[mapView removeOverlays:self.mapView.overlays];
MKRoute *mainRoute = [response.routes firstObject];
routeLine = mainRoute.polyline;
if(routeLine){
[self.mapView removeOverlay:routeLine];
}
//the addOverlay method is not drawing the polyline
[self.mapView addOverlay: routeLine level:MKOverlayLevelAboveRoads];
//proof that route is being created successfully
NSLog(#"Total distance is %f", mainRoute.distance);
MKMapPoint middlePoint = mainRoute.polyline.points[mainRoute.polyline.pointCount/2];
//also, the addannotation method is not being called either it seems like
[self createAndAddAnnotationForCoordinate:MKCoordinateForMapPoint(middlePoint)];
}
}];
}
Our createAndAddAnnotationForCoordinate method,
-(void) createAndAddAnnotationForCoordinate : (CLLocationCoordinate2D)coordinate{
MKPointAnnotation* annotation = [[MKPointAnnotation alloc]init];
annotation.coordinate = coordinate;
annotation.title = #"Point";
annotation.subtitle = #"subtitle";
[mapView addAnnotation:annotation];
}
Our overridden mapviewdelegate method,
-(MKOverlayRenderer )mapView:(MKMapView )mapView rendererForOverlay:(id<MKOverlay>)overlay{
if([overlay isKindOfClass:[MKPolyline class]]){
MKPolylineRenderer *renderer = [[MKPolylineRenderer alloc]initWithOverlay:overlay];
renderer.strokeColor = [UIColor redColor];
renderer.lineWidth = 10.0f;
return renderer;
}
else
return nil;
}
The output if address one was somewhere in NJ and address two was somewhere in CA:
address one lat is 40.902599
address one lon is -74.407097
address two lat is 34.054435
address two lon is -118.253393
Total distance is 4459771.000000
Ron, I'm suspecting your polyline (mainRoute.polyline) - your rendererForOverlay looks almost exactly like one I am using successfully. Barring the very basic mistakes like not setting the MKMapView delegate, or setting it to some other object, I would be almost sure the polyline you add to the map does not contain valid CLLocationCoordinate2D structs.
In Swift, creation goes like
var coordinatePtr = UnsafeMutablePointer<CLLocationCoordinate2D>(track2DCoordinates)
let trackPolygon = MKPolyline(coordinates: coordinatePtr, count: track2DCoordinates.count)
mapView.removeOverlays(mapView.overlays)
mapView.addOverlay(trackPolygon)
Start by verifying that you really have a valid MKPolyline.
I'm also not sure about your middlePoint calculation.
MKMapPoint middlePoint = mainRoute.polyline.points[mainRoute.polyline.pointCount/2];
This kind of thing probably works right now but in Swift you need to be a lot more careful of the data types used as index. What if you have an odd number of points, or zero?

I'm trying to perform an MKLocalSearch, but only searching within a given radius

So let's say I'm trying to search for a query within 1 mile radius of the user's current location. Here's my search code so far:
- (void) performLocalSearch{
MKLocalSearchRequest *searchRequest = [[MKLocalSearchRequest alloc] init];
searchRequest.naturalLanguageQuery = _searchText;
MKCoordinateRegion searchRegion;
searchRegion.center.latitude = userLocation.coordinate.latitude;
searchRegion.center.longitude = userLocation.coordinate.longitude;
searchRegion.span.latitudeDelta = 0.0144927536231884;
searchRegion.span.longitudeDelta = 0.0144927536231884;
searchRequest.region = searchRegion;
MKLocalSearch *search = [[MKLocalSearch alloc] initWithRequest:searchRequest];
[search startWithCompletionHandler:^(MKLocalSearchResponse *response, NSError *error) {
for (MKMapItem *item in response.mapItems) {
MKPointAnnotation *annotation = [[MKPointAnnotation alloc] init];
annotation.title = item.name;
annotation.coordinate = item.placemark.coordinate;
[_map addAnnotation:annotation];
}
}
];
}
When I run the code on my device, pins show up on the map, but they are not within the specified radius. I also tried using MKCoordinateRegionMakeWithDistance to set the search region but I couldn't figure out how to cast the double radius value as a CLLocationDistance object. Any help would be appreciated. Thanks.

Store Current Location in UITableView

There must be an easy way to do this or somewhere I am going wrong but I can't seem to save my current location as a global variable and then add it to my array to populate my UITableView.
Basically, at the moment I have an empty UITableView that is used to populate results from a local search, via a search bar. This part works great. BUT, what I want is to always have the 1st row as the users current location, like in maps, google maps, reminders etc. I figured this would be a simple task but I cant get it to work. Can someone help me please.
I use the following code to get my current location, reverse geocode it, and plot it on the map when the app starts:
self.locationManager = [[CLLocationManager alloc]init];
if ([CLLocationManager locationServicesEnabled]) {
self.locationManager.delegate = self;
self.locationManager.distanceFilter = kCLDistanceFilterNone; // whenever we move
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[self.locationManager startUpdatingLocation];
}
CLLocationCoordinate2D zoomLocation;
zoomLocation.latitude = _locationManager.location.coordinate.latitude;
zoomLocation.longitude= _locationManager.location.coordinate.longitude;
currentCoord.latitude = _locationManager.location.coordinate.latitude;
currentCoord.longitude= _locationManager.location.coordinate.longitude;
//reverse geocoder
CLLocation *currentLocation = [[CLLocation alloc] initWithLatitude:zoomLocation.latitude longitude:zoomLocation.longitude];
CLGeocoder *geoCoder = [[CLGeocoder alloc] init];
[geoCoder reverseGeocodeLocation:currentLocation completionHandler:^(NSArray *placemarks, NSError *error) {
for (CLPlacemark *placemark in placemarks) {
MKPointAnnotation *annotation =
[[MKPointAnnotation alloc]init];
annotation.coordinate = zoomLocation;
annotation.title = #"Current Location";
annotation.subtitle = placemark.name;
// 2
MKCoordinateRegion viewRegion = MKCoordinateRegionMakeWithDistance(zoomLocation, 0.5*METERS_PER_MILE, 0.5*METERS_PER_MILE);
// 3
[_mapView setRegion:viewRegion animated:YES];
[_mapView addAnnotation:annotation];
}
}];
When I run my local search I use the following code to populate my array to load the table with:
MKLocalSearchRequest *request =
[[MKLocalSearchRequest alloc] init];
request.naturalLanguageQuery = _searchBar.text;
request.region = _mapView.region;
MKLocalSearch *search =
[[MKLocalSearch alloc]initWithRequest:request];
[search startWithCompletionHandler:^(MKLocalSearchResponse
*response, NSError *error) {
if (response.mapItems.count == 0)
NSLog(#"No Matches");
else
for (MKMapItem *item in response.mapItems)
{
[tableData addObject:item];
MKPointAnnotation *annotation =
[[MKPointAnnotation alloc]init];
annotation.coordinate = item.placemark.coordinate;
annotation.title = item.placemark.name;
annotation.subtitle = item.placemark.title;
[annotations addObject:annotation];
[_tableResults reloadData];
}
}];
I really can't figure this out. How would I add my current location? Would really appreciate some advise. Thanks in advance guys!
Try this,
[search startWithCompletionHandler:^(MKLocalSearchResponse
*response, NSError *error) {
if (response.mapItems.count == 0)
NSLog(#"No Matches");
else
for (MKMapItem *item in response.mapItems)
{
[tableData addObject:item];
[annotations addObject:[self annotationFromMapItem:item]];
}
double latitude = self.locationManager.location.coordinate.latitude;
double longitude = self.locationManager.location.coordinate.longitude;
MKPlacemark *placemark = [[[MKPlacemark alloc] initWithCoordinate:CLLocationCoordinate2DMake(latitude, longitude) addressDictionary:nil] autorelease];
MKMapItem *mapItem = [[[MKMapItem alloc] initWithPlacemark:placemark] autorelease];
[mapItem setName:Current Location];
[tableData insertObject:mapItem atIndex:0];
[annotations insertObject:[self annotationFromMapItem:mapItem] atIndex:0];
[_tableResults reloadData];
}];
- (MKPointAnnotation) annotationFromMapItem:(MKMapItem *)item {
MKPointAnnotation *annotation =
[[MKPointAnnotation alloc]init];
annotation.coordinate = item.placemark.coordinate;
annotation.title = item.placemark.name;
annotation.subtitle = item.placemark.title;
return annotation;
}

Getting Bad Access error with iOS Annotations and Arrays

The code below is for conducting search with MKLocalSearch and loading the results into an array.
This array gets passed to my [self.mapView addAnnotations:annotations] method. Everything works great until I try to dismiss this viewcontroller by tapping the back button (in my navigation bar for storyboards).
I get EXC_BAD_ACCESS(code=1, address=0x4). If I comment out the Show Pins section below the problem goes away (but of course I am now not loading my annotations).
Please help!
-(void)issueLocalSearchLookup:(NSString *)searchString usingPlacemarksArray:(NSArray *)placemarks {
self.coords = mapView.userLocation.coordinate;
// Set the size of the region we want to get search results for.
MKCoordinateSpan span = MKCoordinateSpanMake(0.001250, 0.001250);
MKCoordinateRegion region = MKCoordinateRegionMake(mapView.userLocation.coordinate, span);
[self.mapView setRegion:region animated:YES];
// Create the search request
self.localSearchRequest = [[MKLocalSearchRequest alloc] init];
self.localSearchRequest.region = region;
self.localSearchRequest.naturalLanguageQuery = searchString;
// Perform the search request...
self.localSearch = [[MKLocalSearch alloc] initWithRequest:self.localSearchRequest];
[self.localSearch startWithCompletionHandler:^(MKLocalSearchResponse *response, NSError *error) {
if(error){
NSLog(#"localSearch startWithCompletionHandlerFailed! Error: %#", error);
return;
} else {
// We are here because we have data!
for(MKMapItem *mapItem in response.mapItems){
// Show pins...
NSMutableArray *annotations = [NSMutableArray array];
Annotation *annotation = [[Annotation alloc] initWithCoordinate: mapItem.placemark.location.coordinate];
annotation.title = mapItem.name;
annotation.subtitle = mapItem.placemark.addressDictionary[(NSString *)kABPersonAddressStreetKey];
[mapView addAnnotation:annotation];
NSLog(#"Name for result: = %#", mapItem.name);
[self.mapView addAnnotations:annotations];
NSLog(#"Name for result: = %#", mapItem.name);
}
MKCoordinateSpan span = MKCoordinateSpanMake(0.01, 0.01);
MKCoordinateRegion region = MKCoordinateRegionMake(self.coords, span);
[self.mapView setRegion:region animated:YES];
}
}];
}
I had set my custom annotation to be a subclass of NKPlacemark....I needed to have it be a subclass of NSObject.

After JSON parsing -viewForAnnotation shows only ONE single annotation on MKMapView

I need to show on my MkMapView about 10 locations (and respective annotations) and after pressing a button I need to add new different annotations according to different JSON parsing results (for example a locationIDValue < 100 means a red pin, otherwise green). This is the simplified code:
- (void)viewDidLoad
{
[super viewDidLoad];
map.showsUserLocation = true;
map.mapType = MKMapTypeStandard;
arrayID = [[NSMutableArray alloc] initWithObjects: #"id1", #"id2", #"id3", #"id4", #"id5", #"id6", #"id7", #"id8", #"id9", #"id10", nil];
#define MakeLocation(lat,lon) [[CLLocation alloc] initWithLatitude:lat longitude:lon]
locations= #[ MakeLocation(lat1,lon1), MakeLocation(lat2,lon2), MakeLocation(lat3,lon3), MakeLocation(lat4,lon4), MakeLocation(lat5,lon5), MakeLocation(lat6,lon6), MakeLocation(lat7,lon7), MakeLocation(lat8,lon8), MakeLocation(lat9,lon9), MakeLocation(lat10,lon10) ];
for (int l=0; l<[locations count]; l++) { // HERE ITS PERFECT! I CAN SEE ALL 10 ANNOTATIONS!
MKPointAnnotation* annotation= [MKPointAnnotation new];
annotation.coordinate = [locations[l] coordinate];
[map addAnnotation: annotation];
}
}
and
- (IBAction)parseMethod {
[map removeAnnotations:map.annotations];
for (int i=0; i < [arrayID count]; i++) { // arrayID contains ID values to parse for each location
NSURL *url = [NSURL URLWithString:
[NSString stringWithFormat:
#"http://JSONPARSINGURL/%#",[arrayID objectAtIndex:i]]];
NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:5.0];
operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
// PARSING CODE . . .
NSMutableArray *value = [JSON objectForKey:#"value"];
NSMutableDictionary *value0 = [value objectAtIndex:0];
[valueID replaceObjectAtIndex:i withObject:[value0 objectForKey:#"valueID"]];
locationIDValue = [[valueID objectAtIndex:i] intValue]; // locationIDValue contains the values that i must use to put different annotations on the map
NSLog(#"locationIDValue: %d", locationIDValue); // here I control IF app parses all the values of locationIDValue
[table reloadData]; // here I put another control to see all values parsed
}
failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
NSLog(#"FAILURE");
}];
NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // I have used also simple [operation start] but I have same issue!
[queue addOperation:operation];
[queue waitUntilAllOperationsAreFinished];
}
NSLog(#"END PARSING"); // here I control the end of parsing, so now I can add new annotations to MKMapView according to locationIDValue array
MKPointAnnotation *annotation = [[MKPointAnnotation alloc] init];
for (int l=0; l<[locations count]; l++) { // HERE I CAN SEE ONLY A SINGLE LOCATION!
annotation.coordinate = [locations[l] coordinate];
NSLog(#"%f - %f", annotation.coordinate.latitude, annotation.coordinate.longitude); // here I control IF app parses all the values of coordinates
[map addAnnotation: annotation];
}
}
and
- (MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
MKPinAnnotationView *pinView=[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"pinView"];
pinView.animatesDrop=YES;
if (annotation != map.userLocation)
{
if ( locationIDValue <= 100) {
pinView.pinColor = MKPinAnnotationColorRed;
return pinView;
}
pinView.pinColor = MKPinAnnotationColorGreen;
return pinView;
}
else
map.userLocation.title = #"My position";
return nil;
}
The situation:
When I open app for the first time its all right, I can see ALL the annotations dropping on the map, according with the #define MakeLocation latitude and longitude; but if I press the button and start the parseMethod, I wait a few seconds (according to NSOperationQueue, but I can test it also without queue, with simple [operation start] code), then I can see ONLY ONE SINGLE annotation on the map, always the same, strangely on the FIFTH location, with lat5-lon5 coordinates (otherwise, if I change a few code I can see ALL THE ANNOTATIONS dropping on the SAME location). As u can see I write some NSLog code and add UITableView to control the activity, and this is the result:
END PARSING
coordinates: lat1 - lon1
coordinates: lat2 - lon2
coordinates: lat3 - lon3
coordinates: lat4 - lon4
coordinates: lat5 - lon5 <--- THE STRANGE POSITION
coordinates: lat6 - lon6
coordinates: lat7 - lon7
coordinates: lat8 - lon8
coordinates: lat9 - lon9
coordinates: lat10 - lon10
locationIDValue: 100
locationIDValue: 90
locationIDValue: 50
locationIDValue: 120
locationIDValue: 20
locationIDValue: 40
locationIDValue: 80
locationIDValue: 180
locationIDValue: 140
locationIDValue: 10
Well, it seems PERFECT: app parse all the data (also a UITableView populate rows with ALL the new values parsed), so AFTER the end of parsing operation I think we have EVERY value necessary to populate map with new annotations. Well, what is wrong here? Why after parsing I can only see a single annotation? Or, ALL annotations dropping ON THE SAME LOCATION? Please give me an help!
In parseMethod, change this:
MKPointAnnotation *annotation = [[MKPointAnnotation alloc] init];
for (int l=0; l<[locations count]; l++) { // HERE I CAN SEE ONLY A SINGLE LOCATION!
annotation.coordinate = [locations[l] coordinate];
NSLog(#"%f - %f", annotation.coordinate.latitude, annotation.coordinate.longitude); // here I control IF app parses all the values of coordinates
[map addAnnotation: annotation];
}
to this:
for (int l=0; l<[locations count]; l++) { // HERE I CAN SEE ONLY A SINGLE LOCATION!
//create a NEW annotation for each location...
MKPointAnnotation *annotation = [[MKPointAnnotation alloc] init];
annotation.coordinate = [locations[l] coordinate];
NSLog(#"%f - %f", annotation.coordinate.latitude, annotation.coordinate.longitude); // here I control IF app parses all the values of coordinates
[map addAnnotation: annotation];
}
Move the MKPointAnnotation *annotation = [[MKPointAnnotation alloc] init] to inside the for-loop so that a new annotation object is created for each location.
The existing code there creates a single annotation object and just keeps modifying its coordinate.
What is also really important to note (because it was very hard to find that error and I sat the whole last night in front of this...) is to check if your object that conforms to MKAnnotation has some master base class that imlements the methods isEqual and / or hash on only the data of the base class and you do not overwrite that in your base class. This was my problem and thus isEqual returned YES for all annotations and THUS it showed always only one annotation on the map. I couldn't find any hint on the internet about this so I will leave at least this comment here for poor people in the same situation.

Resources