I have a tab on the RootViewController.m. The tab has 2 buttons. The first button upon click will go to CorpViewcontroller which has the mapView on it. When I click on the first button on the first try, the map is blank with google label on the bottom. I have to click back then click on the button again then the map show up. Is it possible to always show the map on the first button click?
My rootViewController.m to go to the second screen:
[self.navigationController pushViewController:self.corpController animated:YES];
The second screen called corpViewController has the following code:
- (void)viewDidLoad
{
[super viewDidLoad];
self.title = #"Set Remote Location";
self.jsonData = [[NSMutableData alloc] init];
mapView.showsUserLocation = YES;
//Setup the double tap gesture for getting the remote location..
UITapGestureRecognizer *tgr = [[UITapGestureRecognizer alloc]
initWithTarget:self action:#selector(handleGesture:)];
tgr.numberOfTapsRequired = 2;
tgr.numberOfTouchesRequired = 1;
[mapView addGestureRecognizer:tgr];
mapView.delegate = self;
NSLog(#"viewDidLoad done");
}
- (void)viewWillAppear:(BOOL)animated {
NSLog(#"viewWillAppear");
appDelegate = (NBSAppDelegate *)[[UIApplication sharedApplication] delegate];
double curLat = [appDelegate.curLat doubleValue];
MKUserLocation *userLocation = mapView.userLocation;
double miles = 10.0;
double scalingFactor = ABS( (cos(2 * M_PI * curLat / 360.0) ));
MKCoordinateSpan span;
span.latitudeDelta = miles/69.0;
span.longitudeDelta = miles/(scalingFactor * 69.0);
MKCoordinateRegion region2;
region2.span = span;
region2.center = userLocation.coordinate;
[mapView setRegion:region2 animated:YES];
NSLog(#"viewWillAppear done..");
}
Please Advise.
Thank you
Are you initializing the MapView in the viewDidLoad method in your view controller?
If so, try moving it to the viewDidAppear method. That worked for me.
In viewDidLoad you are setting showsUserLocation to YES and in viewWillAppear, you are zooming into the mapView.userLocation coordinate.
The userLocation property isn't usually ready with a valid coordinate immediately after setting showsUserLocation to YES.
The first time you show the view controller, it's still invalid and you are zooming into the coordinate 0,0.
By the time you show the view controller a second time, the user location has been obtained and the coordinate is valid.
Instead of zooming into the user location in viewWillAppear, do it in the delegate method mapView:didUpdateUserLocation: which the map view calls when it gets a user location update.
In addition, you also probably want to move the mapView.showsUserLocation = YES; to viewWillAppear and in viewWillDisappear, set it to NO. This way, the map view will zoom in to the user location every time the view controller is shown instead of just the first time.
An unrelated point is that to zoom in to a specific distance, it's much easier to use the MKCoordinateRegionMakeWithDistance function instead of trying to convert miles to degrees yourself.
Here's an example of the changes suggested in corpViewController:
- (void)viewWillAppear:(BOOL)animated
{
//move this from viewDidLoad to here...
mapView.showsUserLocation = YES;
}
-(void)viewWillDisappear:(BOOL)animated
{
mapView.showsUserLocation = NO;
}
-(void)mapView:(MKMapView *)mv didUpdateUserLocation:(MKUserLocation *)userLocation
//Changed the **internal** parameter name from mapView to mv
//to avoid a compiler warning about it hiding instance var with same name.
//It's better to use the passed parameter variable anyway.
{
NSLog(#"didUpdateUserLocation");
double miles = 10.0;
//Instead of manually calculating span from miles to degrees,
//use MKCoordinateRegionMakeWithDistance function...
//Just need to convert miles to meters.
CLLocationDistance meters = miles * 1609.344;
MKCoordinateRegion region2 = MKCoordinateRegionMakeWithDistance
(userLocation.coordinate, meters, meters);
[mv setRegion:region2 animated:YES];
}
Related
I'm implementing search in my MKMapView and I've faced two problems:
When I perform search, location appears in the result and map moves to the found location only after I start to move to the destination. This happens, when the search results are out of the view bounds. If they are inside of the map view bounds or near them it's fine.
It "hops" all the time from one search result to another or to the user's location. I don't expect such behaviour from it.
I've tried several things and I suppose, that the problem is in: didAddAnnotationViews:
[self.locationManager stopUpdatingLocation];
MKAnnotationView *annotationView = [views objectAtIndex:0];
NSLog(#"_Here_ %#", [views description]);
id<MKAnnotation> mp = [annotationView annotation];
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance([mp coordinate], 250, 250);
[mv setRegion:region animated:YES];
[self.mapView selectAnnotation:mp animated:YES];
Though, I also thought that the problem is in didUpdateToLocation, so I disable updating after the first pin is drop (by search or by tap):
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation {
double miles = 0.3;
double scalingFactor =
ABS( cos(2 * M_PI * newLocation.coordinate.latitude /360.0) );
MKCoordinateSpan span;
span.latitudeDelta = miles/69.0;
span.longitudeDelta = miles/( scalingFactor*69.0 );
MKCoordinateRegion region;
region.span = span;
region.center = newLocation.coordinate;
[self.mapView setRegion:region animated:YES];
self.mapView.showsUserLocation = YES;
}
Finally, search method:
-(void)searchBarSearchButtonClicked:(UISearchBar *)theSearchBar
{
MKLocalSearchRequest *searchRequest = [[MKLocalSearchRequest alloc] init];
[searchRequest setNaturalLanguageQuery:theSearchBar.text];
searchRequest.region = MKCoordinateRegionMakeWithDistance(self.mapView.userLocation.coordinate, 1000, 1000);
MKLocalSearch *localSearch = [[MKLocalSearch alloc] initWithRequest:searchRequest];
[localSearch startWithCompletionHandler:^(MKLocalSearchResponse *response, NSError *error) {
if (!error) {
NSMutableArray *annotations = [NSMutableArray array];
[response.mapItems enumerateObjectsUsingBlock:^(MKMapItem *item, NSUInteger idx, BOOL *stop) {
for (id<MKAnnotation>annotation in self.mapView.annotations)
{
if (annotation.coordinate.latitude == item.placemark.coordinate.latitude &&
annotation.coordinate.longitude == item.placemark.coordinate.longitude)
{
return;
}
}
MKPointAnnotation *addAnnotation = [[MKPointAnnotation alloc] init];
addAnnotation.title = [item.placemark.addressDictionary objectForKey:#"Street"];
addAnnotation.coordinate = item.placemark.coordinate;
[annotations addObject:addAnnotation];
}];
for (id<MKAnnotation>annotation in self.mapView.annotations) {
[self.mapView removeAnnotation:annotation];
}
[self.mapView addAnnotations:annotations];
} else {
NSLog(#"Search Request Error: %#", [error localizedDescription]);
}
}];
//Hide the keyboard.
[self.searchBar resignFirstResponder];
}
My aim is to create a MapView, where user can pin the location by tap or via search and, obviously, see the search result.
For the first problem:
When I perform search, location appears in the result and map moves to
the found location only after I start to move to the destination. This
happens, when the search results are out of the view bounds. If they
are inside of the map view bounds or near them it's fine.
This happens because you are moving the map to the annotations found (at least the first one) in the didAddAnnotationViews delegate method.
But that delegate method is only called when an annotation is in the visible area. If an annotation is added to the map but it's not in the visible area (yet), viewForAnnotation won't get called and therefore didAddAnnotationViews won't get called.
Then, when you manually move the map, the annotations that were added start coming into the visible area and then the delegate method gets called and suddenly the map jumps to one of those annotations.
Don't call setRegion inside the didAddAnnotationViews delegate method.
Sometimes, doing so can also cause an endless cycle of viewForAnnotation and didAddAnnotationViews calls because when the region is changed, it causes other annotations to come into view that weren't previously, so viewForAnnotation gets called and then didAddAnnotationViews gets called, and so on.
Instead, set the region right after you call addAnnotations: (or better, just call showAnnotations:) in the searchBarSearchButtonClicked: method.
I would also remove the call to stopUpdatingLocation from didAddAnnotationViews. You probably don't even need the location manager at all if you set the map's showsUserLocation to YES.
For the second problem:
It "hops" all the time from one search result to another or to the
user's location. I don't expect such behaviour from it.
This is also partly due to calling setRegion in didAddAnnotationViews but also because setRegion is called in didUpdateToLocation.
So for the reason described for the first problem, the two delegate methods and the user's manual movements are fighting with each other and the map ends up hopping around.
Don't call setRegion in the didUpdateToLocation method (or, call it once by keeping track in a BOOL whether you've already zoomed to the user location or not).
Not affecting the behavior, but setting showsUserLocation to YES in the didUpdateToLocation doesn't make sense. Why not set this in viewDidLoad or turn it on in the storyboard/xib?
Also, there's no need to calculate the region span manually like that (it's better to let the MapKit do that work for you). Just convert the miles to meters and call MKCoordinateRegionMakeWithDistance.
I have an app that loads a tabbarcontroller with 3 tabs. One of them is a mapview. It is set to zoom into the user's location by this code:
- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
_mapView.showsUserLocation = YES;
CLLocationCoordinate2D zoomLocation;
//IF no city was selected, use userLocation as center
if (!self.cityWasSelected) {
zoomLocation.latitude = self.userLocation.coordinate.latitude;
zoomLocation.longitude = self.userLocation.coordinate.longitude;
CLLocationDistance visibleDistance = 5000; // 5 kilometers
MKCoordinateRegion adjustedRegion = MKCoordinateRegionMakeWithDistance(zoomLocation, visibleDistance, visibleDistance);
[_mapView setRegion:adjustedRegion animated:YES];
} else { //if a city was selected, use that city's value...this is actually the same right now, since self.userLocation is set appropriately elsewhere.
//Set location from selection - forward geocode
zoomLocation.latitude = self.userLocation.coordinate.latitude;
zoomLocation.longitude = self.userLocation.coordinate.longitude;
CLLocationDistance visibleDistance = 5000; // 5 kilometers
MKCoordinateRegion adjustedRegion = MKCoordinateRegionMakeWithDistance(zoomLocation, visibleDistance, visibleDistance);
[_mapView setRegion:adjustedRegion animated:YES];
}
}
The initial tab is a tableviewcontroller for user preferences, the second tab is the mapview and the third is a tableview. When I first tap on the mapview, the map shows the entire world :) If i tap back to the initial tab or the list tab and then return to the mapview, the map is properly centered around my current location.
Why does this happen?
Setting showsUserLocation on the map view will start searching for the user's location. This is an asynchronous operation and you cannot assume that mapView.userLocation will be valid immediately after settings showsUserLocation to YES.
The first time you view appears, you ask the map view to start location. mapView.userLocation probably returns nil. The second time around, the map view has probably finally gotten the user location and loads your map region successfully. This is sheer luck, and you should not rely that you will get location the second time (it may fail, take longer than usual).
When the map view has determined user location, it will call back on a delegate method (MKMapViewDelegate). You need to implement this delegate method like so:
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation {
// We have location, do your logic of setting the map region here.
...
}
Hope this helps.
Everytime I start the app, the first time seeing the map results in a default map that is always zoomed out with no annotations. When I go back on the navigation controller and go back into the map, it now shows the correct region with the appropriate pins. The code I use to add the
- (void) zoomIn {
mapView.showsUserLocation = YES;
CLLocationCoordinate2D annotation;
annotation.latitude = 47.640071;
annotation.longitude = -122.129598;
MKPointAnnotation *annoPoint = [[MKPointAnnotation alloc] init];
annoPoint.coordinate = annotation;
annoPoint.title = #"name";
[mapView addAnnotation:annoPoint];
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(annotation, 500, 500);
[mapView setRegion:region animated:YES];
}
I call this block of code from the viewDidLoad, but it only works after I go back to the main page from the navigation controller and enter this UIViewController again.
Does anyone know what the problem is or have seen it before?
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
// this delegate fonction is called when the userlocation is updated
// try to move your code here
}
you have also
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
{
}
hope this helps
Show us your viewDidLoad function, you're probably calling zoomIn too early, maybe before your MKMapView has been initialized.
When a user selects a menu option I currently have the location name sent to my map view via "stringToDisplay" and a pin drops on the map as expected, however, it does not center the pin or zoom on the location on the first attempt. Every attempt after the first is flawless. How can I make the map zoom to my location(s) on the first attempt? Your input is greatly appreciated.
I have multiple IF statements contained in my viewWillAppear, but see below for one example of a location which experiences this problem:
-(void)viewWillAppear:(BOOL)animated
{
CLLocationCoordinate2D location;
NSLog(#"self.stringToDisplay = %#", self.stringToDisplay);
MKCoordinateRegion region;
MKCoordinateSpan span;
span.latitudeDelta=0.1;
span.longitudeDelta=0.1;
region.span=span;
region.center=location;
[mapView setRegion:region animated:TRUE];
[mapView regionThatFits:region];
if ([self.stringToDisplay isEqualToString: #"August First Bakery & Café"])
{
location.latitude = (double) 44.475486;
location.longitude = (double) -73.2172641;
MapViewAnnotation *newAnnotation = [[MapViewAnnotation alloc] initWithTitle:#"August First Bakery & Café" andCoordinate:location];
[self.mapView addAnnotation:newAnnotation];
[mapView setCenterCoordinate:location animated:YES];
}
}
Set a breakpoint in the viewWillAppear. Check to see if mapView is nil when you're doing the setRegion/regionThatFits. If it is you may need to move the code to viewDidAppear. - compliments of Phlibbo
Very much a newbie here so please forgive the ignorance. I have spent some time trying to understand what I am missing but cannot figure it out.
My app centers over Washington State when loading but when I try to zoom to the users current location it puts me at latitude 0 longitude 0. If I comment out the "// startup: center over WA" section it centers over the users current location and then goToLocation works fine.
How do I get it to center over Washington State and then zoom to the users current location upon clicking goToLocation?
Thanks!
- (void)viewDidLoad {
[super viewDidLoad];
[self loadOurAnnotations];
[mapView setShowsUserLocation:NO];
// startup: center over WA
CLLocationCoordinate2D defaultCoordinate;
defaultCoordinate.latitude = 47.517201;
defaultCoordinate.longitude = -120.366211;
[mapView setRegion:MKCoordinateRegionMake(defaultCoordinate, MKCoordinateSpanMake(6.8, 6.8)) animated:NO];
}
-(IBAction)goToLocation {
MKUserLocation *myLocation = [mapView userLocation];
CLLocationCoordinate2D coord = [[myLocation location] coordinate];
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(coord, 350, 350);
[mapView setRegion:region animated:YES];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:1];
[UIView commitAnimations];
}
First, to use userLocation in the MKMapView at all, you have to pass YES to setShowsUserLocation (not NO).
Next thing is that after turning showsUserLocation on, it may take a few seconds or more for the map view to determine the location and set userLocation. Until then, the location will be nil (giving coordinates of 0,0).
To really know when the userLocation is ready (or updated), implement the didUpdateUserLocation delegate method. It's also helpful to implement the didFailToLocateUserWithError method in case of a problem determining the user location.
However, in your case, you could just do the following in the goToLocation method:
MKUserLocation *myLocation = [mapView userLocation];
if (myLocation.location == nil)
{
NSLog(#"user location has not been determined yet");
return;
}
CLLocationCoordinate2D coord = [[myLocation location] coordinate];
MKCoordinateRegion region = ... //rest of the code stays the same
The animation statements at the end of that method don't do anything, by the way.