I need some help with an app i'm making using MapKit
I'm struggling with the didUpdateUserLocation: - it keeps randomly crashing the app. When I comment out all of the code it works perfectly but this isn't a suitable situation. I've been fiddling around with it all but still no real success.
Here's my code
-(void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation {
NSLog(#"update loc");
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(userLocation.coordinate, 250.0, 250.0);//MKCoordinateRegionMake(userLocation.coordinate, mapView.region.span);
if (first) {
region = MKCoordinateRegionMakeWithDistance(userLocation.coordinate, 250.0, 250.0);
first = false;
}
if (!CLLocationCoordinate2DIsValid(userLocation.coordinate)) {
//do nothing, invalid regions
NSLog(#"co-ord fail");
} else if (region.span.latitudeDelta <= 0.0 || region.span.longitudeDelta <= 0.0) {
NSLog(#"invalid reg");
} else {
[mapView setRegion:region animated:NO];
}
}
The app never hits "co-ord fail" or "invalid reg" so I don't really know what the problem is since I set the values myself.
The problem usually occurs when the nib for the Map is closed and it has dealloced the view.
I have seen some other suggestions but haven't been confident about their work. Ideally I'd like to use mapView.region.span so to maintain the zoom levels
Many Thanks,
James
Watch out that if an App that was suspended a short while, on resume the
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
delegate receives as the first userLocation (0.0,0.0) as latitude,longitude! I think that is a bug.. Check:
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation {
NSLog( #"userLocation:(%p) %#",userLocation,userLocation);
NSLog( #"userLocation.location:(%p) %#",userLocation.location,userLocation.location);
NSLog( #"userLocation.coordinate: %f,%f valid:%d",userLocation.coordinate.latitude,userLocation.coordinate.longitude, CLLocationCoordinate2DIsValid(userLocation.coordinate));
NSLog( #"userLocation.location.coordinate: %f,%f valid: %d",userLocation.location.coordinate.latitude,userLocation.location.coordinate.longitude, CLLocationCoordinate2DIsValid(userLocation.location.coordinate));
Result:
userLocation:(0x193fc0) <MKUserLocation: 0x193fc0>
userLocation.location:(0x0) (null)
userLocation.coordinate: -180.000000,-180.000000 valid:0
userLocation.location.coordinate: 0.000000,0.000000 valid: 1
Odd that 0,0 is produced from userLocation.location.coordinate while userLocation.location is nil, resulting in a valid coordinate, even!
Indeed the check on userLocation.location being nil seems best.
The problem usually occurs when the nib for the Map is closed and it
has dealloced the view.
[mapView setRegion:region animated:NO];
Well isn't that your problem then?
This is why we have dependency inversion patterns like Observer/Listener.
Instead of fiddling around with the view directly with your CLLocationManager delegate, use NSNotification to alert subscribing views (such as your MapView) that it should update. If the MapView has been dealloced it won't do anything.
Related
I have an app that has a map with a lot of markers on it, the app notifies the user whenever he approaches one of the markers. I'm using Google Maps iOS SDK for the map, that's mean I use GMSMapView for the map view (mapView).
I want that when the user opens the app via a notification (tap on a notification) the map's camera will automatically be pointing to the notification's marker.
I've tried to do it like this:
AppDelegate.m:
-(void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
NSString *markerIdentifier=[[notification userInfo] objectForKey:#"markerIdentifier"];
GMSMarker *marker=[self markerForIdentifier:markerIdentifier];
[self.aroundersVC.mapView setSelectedMarker:storeMarker];
//Important part:
[self.aroundersVC animateMapToMarkerPosition:storeMarker];
}
AroundersViewController:
-(void)animateMapToMarkerPosition:(GMSMarker*)marker
{
GMSCameraPosition *currentLocationCameraPosition=[GMSCameraPosition cameraWithLatitude:marker.position.latitude longitude:marker.position.longitude zoom:16];
[self.mapView animateToCameraPosition:currentLocationCameraPosition];
}
But it's not working for some reason, anyone knows why?
Thanks!
Use GMSMapview delegate
-(void)mapView:(GMSMapView *)mapView didChangeCameraPosition:(GMSCameraPosition *)position
{
// Do your stuff here
}
This should solve your problem.
Is there any alternative to get User location and do GeoFence rather than using Location Services in iOS. I am asking this because, when we enable Location Services, battery consumption is too high. That's why I am looking for an alternative. What can I try?
If you are using map then, no need to implement location service code to capture current location, set YES to showUserLocation property of MKMapView
[self.mapView setShowsUserLocation:YES]; and implement its delegate method:
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
`
[self.mapView setShowsUserLocation:YES];
[self.mapView setDelegate:self];
[self.mapView setMapType:MKMapTypeStandard];
[self.view addSubview:self.mapView];
[self.view bringSubviewToFront:self.viewLiveDrive];`
delegate :
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation{
NSLog(#"Location Current : %# ",userLocation.location);
[self.mapView setCenterCoordinate:userLocation.location.coordinate animated:YES];
}
Now you can enjoy with current location :)
I am using am mapview and sometimes my map will zoom onto my users location when I open it but sometimes it will zoom to the middle of an ocean. I don't know what is causing this, this is the code I am using for zooming. I don't want the map to track the user but just zoom to their location once they open the map
-(void) viewDidAppear:(BOOL)animated{
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(self.mapView.userLocation.coordinate, 600.0f, 600.0f);
[self.mapView setRegion:region animated:YES];
}
-(void) viewDidLoad{
[super viewDidLoad];
self.mapView.delegate = self;
[self.mapView setShowsUserLocation:YES];
}
I encountered this issue before. It seems that mapView is slow to load and detect user location sometimes, resulting in your code in viewDidAppear being executed before the map view can check user's location. Thus, the spot in the ocean.
It will be better to use mapView's delegate to display user location when it's ready:
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
if(isShowUserLocation)
{
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(userLocation.coordinate, 600.0, 600.0);
[self.mapView setRegion:[self.mapView regionThatFits:region] animated:YES];
isShowUserLocation = NO;
}
}
Set isShowUserLocation = YES in viewDidLoad. This ensures the user location is shown once on entry and also selectively when you need it.
Edit 1:
#implementation MapViewController
{
BOOL isShowUserLocation;
}
-(void) viewDidLoad
{
[super viewDidLoad];
self.mapView.delegate = self;
[self.mapView setShowsUserLocation:YES];
isShowUserLocation = YES;
}
Edit 2:
Alternatively, use CLLocationManager - see this post. It allows you stop the updating. Do include CoreLocation.framework. You may need to handle some nitty-gritty issues when interacting CLLocationManager with MapView though.
When you pass 0's for lat and long, you will git a spot in the middle of the ocean south of Ghana in Africa.
From a detail view of an event I want to go to a MapViewController, zoom in to the annotation, and open it's callout.
Here is some of the relevant code:
#interface MapViewController : UIViewController<MKMapViewDelegate>
...
- (void) viewWillAppear:(BOOL)animated
{
[self displayAnnotations];
}
- (void) viewDidAppear:(BOOL)animated
{
...
// Zoom in to event
[map setRegion:region animated:YES];
}
- (void) mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
{
...
regionAnimationEnded = YES;
[self selectAnnotation:a];
...
}
- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views
{
...
// check to see if the right view is in the array
...
annotationViewDidAppear = YES;
[self selectAnnotation:a];
...
}
- (void) selectAnnotation:(id<MKAnnotation>)annotation
{
if(annotationViewDidAppear && regionAnimationEnded)
{
if(!openedAnnotationFirstTime)
{
[map selectAnnotation:annotation animated:YES];
openedAnnotationFirstTime = YES;
}
}
}
This works on the ios 6 simulator, but on the ios 5.1 simulator (and on the device) the annotion view isn't visible as it says in the docs:
(void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views
By the time this method is called, the specified views are already added to the map.
So it depends which finishes first: if the region change animation finishes last and the annotation view has appeared it works, otherwise it doesn't.
Any help would be appreciated.
Your selectAnnotation: method is being called twice. Try calling it only once, in mapView:regionDidChangeAnimated: delegate method.
I ended up using perform selector with delay on the actual [map selectAnnotation:annotation animated:YES];
It's a work-around but seems to work nicely.
I have an MKMapView as part of a Navigation Controller in a Tab Bar based app.
I click a UIButton on the first View Controller and it pushes to the second View Controller which contains the MKMapView. When the Map View loads, it zooms in on the user's location using:
- (void)mapView:(MKMapView *)theMapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
if ( !initialLocation )
{
self.initialLocation = userLocation.location;
MKCoordinateRegion region;
region.center = theMapView.userLocation.coordinate;
region.span = MKCoordinateSpanMake(2.0, 2.0);
region = [theMapView regionThatFits:region];
[theMapView setRegion:region animated:YES];
}
}
When I hit the back button on the Navigation Controller above the MapView and then click back to the map, it no longer zooms in on the user's current location, but just has the full zoom out default:
Here's a picture of the view the second time.
I figure it would work correctly if I could somehow call the didUpdateUserLocation in the viewDidAppear method but I'm not sure how to pull this off since the didUpdateUserLocation is a delegate method.
Is that the right approach or is there a different approach I should take to do this? Thanks!
P.S. I've seen this question but it's slightly different with it's use of a modal view controller
I would pull all of the zooming code into its own method that can be messaged from -viewDidAppear: and -mapView:didUpdateToUserLocation:.
- (void)zoomToUserLocation:(MKUserLocation *)userLocation
{
if (!userLocation)
return;
MKCoordinateRegion region;
region.center = userLocation.location.coordinate;
region.span = MKCoordinateSpanMake(2.0, 2.0); //Zoom distance
region = [self.mapView regionThatFits:region];
[self.mapView setRegion:region animated:YES];
}
Then in -viewDidAppear:...
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self zoomToUserLocation:self.mapView.userLocation];
}
And in the -mapView:didUpdateToUserLocation: delegate method...
- (void)mapView:(MKMapView *)theMapView didUpdateToUserLocation:(MKUserLocation *)location
{
[self zoomToUserLocation:location];
}