MkMapView shows a blue screen - ios

I am able to successfully show the user their current location on a MapView. However when I go to another view controller and come back to my mapview, I see a blue screen. Why?
This is my code:
- (void)viewWillAppear:(BOOL)animated {
MyManager * myManager = [MyManager sharedInstance];
//if coming back from another screen, lets load the coordinates
if (myManager.centerOfMap) {
NSLog(#"myManager.centerOfMap has a value:");
self.centerOfMap = myManager.centerOfMap;
}
CLLocationCoordinate2D zoomLocation;
zoomLocation = *(self.centerOfMap);
MKCoordinateRegion viewRegion = MKCoordinateRegionMakeWithDistance(zoomLocation, 0.5*METERS_PER_MILE, 0.5*METERS_PER_MILE);
MKCoordinateRegion adjustedRegion = [_mapView regionThatFits:viewRegion];
[_mapView setRegion:adjustedRegion animated:YES];
}

A blue screen is often a sign that you're point to (0,0) off the coast of Africa. Try printing out the coordinates of centerOfMap

At the point you come back, is the MKMapView allocated or does it show nil in the debugger?
Not sure? When you come back to your view controller, in the code go ahead and set a breakpoint and, in the console, type "po _mapView". Look at the result.
If it is nil, you probably have to Alloc/Init it. What's probably going on is that ARC is automatically flushing out the MKMapView in order to save memory.
Just a question, are you presenting the new View Controller in a modal way using [yourMainViewController presentViewController:newViewController animated:YES]?

This bit sets off alarm bells:
CLLocationCoordinate2D zoomLocation;
zoomLocation = *(self.centerOfMap);
This presumably corresponds to
#property (nonatomic, assign) CLLocationCoordinate2D * centerOfMap;
Which means it's highly likely that it now points to a random bit of stack, which has since been overwritten. It is highly likely that the high-order word is between 0 and 0x4000000 (0 and a billion - most integers and memory addresses will be in this range), which means you end up with a double between (approximately) 0 and 2.
Get rid of the *.

Related

Current location not obtained in here maps

i am using here maps in my app and works fine. i pass destination coordinate from one view controller to another viewcontroller which contains here map and its methods. i tried to get the current location inside the method in the second view controller to calculate the roue, but current location always returns 0,0.please advice how to get current location inside the method where i pass the values from first viewcontroller
viewcontroller1:
- (IBAction)btn_navigate:(id)sender
{
NSLog(#"%#",_POiarray);
_VC=[[NaviMeVC alloc]init];
[_VC GetPoi:_POiarray];
[self.navigationController pushViewController:_VC animated:YES];
}
mapviewcontroller:
-(void)GetPoi:(NSMutableArray *)anArray
{
//get current location
NMAGeoPosition * position=[[NMAGeoPosition alloc]init];
position = [[NMAPositioningManager sharedPositioningManager] currentPosition];
_StartCoordinate=[[NMAGeoCoordinates alloc]initWithLatitude:position.coordinates.latitude longitude:position.coordinates.longitude];
//not working returns nil
[[NMAMapLoader sharedMapLoader] setDelegate:self];
_SelectedPOi=[[NSMutableArray alloc]init];
_SelectedPOi=anArray;
NSString *lat=[anArray valueForKey:#"latitude"];
CLLocationDegrees latitu=[lat doubleValue];
NSString *longi=[anArray valueForKey:#"longitude"];
CLLocationDegrees longit=[longi doubleValue];
_DestinationCoordinate=[[NMAGeoCoordinates alloc]initWithLatitude:latitu longitude:longit];
NSLog(#"%f %f",_DestinationCoordinate.latitude,_DestinationCoordinate.longitude);
NSNumber *lati = [NSNumber numberWithDouble:latitu];
NSNumber *longitu = [NSNumber numberWithDouble:longit];
NSDictionary *userLocation=#{#"lat":lati,#"long":longitu};
NMARoutingMode* routingMode = [[NMARoutingMode alloc] initWithRoutingType:NMARoutingTypeShortest
transportMode:NMATransportModeCar
routingOptions:0];
[self CalculateRoute:routingMode];
}
To start receiving positioning updates you need to call NMAPositioningManager startPositioning which I don't see in your sample code. More details in user guide link below. Please read the other instructions on that page to see if anything helps.
Another thing to check: have you added NSLocationWhenInUseUsageDescription into your projects Info.plist file to ensure your app can receive user location from CLLocationManager?
Also, this may seem obvious but please keep in mind that you will only be able to receive a valid value for currentPosition if you have a position fix.
Positioning User Guide
NMAPositioningManager Doxygen

Can't set map region in iPad app

I have an iPad app that has a feature that shows a region in a MKMapView along with the user's location. This used to work fine, but now it always displays the entire U.S. as the region no matter what I set it as. It does however correctly show the user's location.
The code that sets the region is...
MKCoordinateSpan span = {.latitudeDelta = latSpan, .longitudeDelta = longSpan};
MKCoordinateRegion mapRegion = {center, span};
[self.mapView setRegion:mapRegion];
The values for these variables are...
center = {latitude = 37.525553000000002, longitude = -121.9351005}
latSpan = 0.0029800000000008708
longSpan = 0.011071000000001163
I've double checked, and the center is exactly the location I want, but it doesn't matter what values I put for any of these, I always just get the entire U.S. as the region.
Any ideas what has changed or what I overlooked?
I would really appreciate any help as this driving me crazy...
MapKit will be unable to zoom into the user's location if authorization is not provided by the user.
The result can be that only a display of the United States is shown.
Request permission using:
let locationManager = CLLocationManager()
locationManager.requestWhenInUseAuthorization()
Also add the key NSLocationWhenInUseUsageDescription to your target's Info.plist. The type is a string and is shown to the user during the authorization request.
Not to put too fine a point on it: you're lying. I ran your code with your values and it works perfectly:
The code for this app consists entirely of your values and your code:
#import "ViewController.h"
#import MapKit;
#interface ViewController ()
#property (weak, nonatomic) IBOutlet MKMapView *mapView;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
CLLocationCoordinate2D center = {37.525553000000002, -121.9351005};
double latSpan = 0.0029800000000008708;
double longSpan = 0.011071000000001163;
MKCoordinateSpan span = {.latitudeDelta = latSpan, .longitudeDelta = longSpan};
MKCoordinateRegion mapRegion = {center, span};
[self.mapView setRegion:mapRegion];
}
#end
So clearly you are not telling the truth; you're leaving out something important about what you're doing, because when I do what you say you're doing, I get the right result.

MKMapView and best way to zoom in on user's location

Hey so I have a map view that zooms in on the users location upon view load. Currently I find the users location using the CLLocationManager and initialize the map in the didUpdateLocations delegate method. However, as this is called multiple times I use a global BOOL that is set to true after the map is set once and then never set it again. Here is the code:
Define global BOOL and set it to false in the viewDidLoad:
#implementation MainMapViewController
{
BOOL hasInitializedMap;
}
- (void)viewDidLoad
{
[super viewDidLoad];
hasInitializedMap = NO;
}
I then get the user's location and set the maps zoom inside the (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations delegate method:
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
CLLocation *location = [locations lastObject];
[self initializeMainMapView:location.coordinate.longitude latitude:location.coordinate.latitude];
}
- (void)initializeMainMapView:(float)longitude latitude:(float)latitude
{
if (!hasInitializedMap)
{
CLLocationCoordinate2D zoomLocation;
zoomLocation.latitude = latitude;
zoomLocation.longitude = longitude;
MKCoordinateRegion viewRegion = MKCoordinateRegionMakeWithDistance(zoomLocation, 1500, 1500);
[mainMapView setRegion:viewRegion animated:YES];
hasInitializedMap = YES;
}
}
This code works but has left me wondering if there is a better way to do this. I hate using global flags like this as it can make your code confusing and is sloppy at best. Is there a better way to do what I am trying to do? Is there a delegate method that only gets called when the users position is updated for the first time? Can I do this with just the MKMapView and forget CoreLocation altogether?
I've done it a few ways... in fact the framework has been changed a few times so I can't remember how I settled on it. The MKMapkit does have a method for showing user location, developer doc:
This property does not indicate whether the user’s position is actually visible on the map, only whether the map view should try to display it. Setting this property to YES causes the map view to use the Core Location framework to find the current location and try to display it on the map. As long as this property is YES, the map view continues to track the user’s location and update it periodically. The default value of this property is NO.
Showing the user’s location does not guarantee that the location is visible on the map. The user might have scrolled the map to a different point, causing the current location to be offscreen. To determine whether the user’s current location is currently displayed on the map, use the userLocationVisible property.
The user's location will appear as an annotation. You can get the annotation and zoom in on that. what would be better would be to use that flag in conjunction with the MKMapViewDelegate which has a method -mapView:DidUpdateUserslocation:
delegate documentation
Maybe after you get the first one you can change the map's tracking mode to none (i'm not sure if that removes the user location annotation or not, if it does, you can easily drop a map pin there with the proper skin). -setUserTrakingMode:animated: is the method on MKMap can use for that.
One thing I would caution on, in writing apps that work with user location in the past, it take a few seconds to get an accurate location. You might want to let the GPS ping a few times first before you lock in on a position. I've found that the first few ticks can be wildly inaccurate. Hope that helps.

How to load only few annotations around current location?

I read through many topic but i don't think i really get them.
I found the following code in one of the topic but I am not sure how to put it. Should i put in viewdidload? or somewhere else?
And I have a large annotation data of 3000 dots. But I dont know how to read a sqlite data (I am using a plist data) Please give me a help.
Thanks in advance.
// create and populate an array containing all potential annotations
NSMutableArray *allPotentialAnnotations = [NSMutableArray array];
for(all potential annotations)
{
MyAnnotation *myannotation = [[MyAnnotation alloc]
initWithCoordinate:...whatever...];
[allPotentialAnnotations addObject:myannotation];
[myannotation release];
}
// set the user's current location as the reference location
[allPotentialAnnotations
makeObjectsPerformSelector:#selector(setReferenceLocation:)
withObject:mapView.userLocation.location];
// sort the array based on distance from the reference location, by
// utilising the getter for 'distanceFromReferenceLocation' defined
// on each annotation (note that the factory method on NSSortDescriptor
// was introduced in iOS 4.0; use an explicit alloc, init, autorelease
// if you're aiming earlier)
NSSortDescriptor *sortDescriptor =
[NSSortDescriptor
sortDescriptorWithKey:#"distanceFromReferenceLocation"
ascending:YES];
[allPotentialAnnotations sortUsingDescriptors:
[NSArray arrayWithObject:sortDescriptor]];
// remove extra annotations if there are more than five
if([allPotentialAnnotations count] > 5)
{
[allPotentialAnnotations
removeObjectsInRange:NSMakeRange(5,
[allPotentialAnnotations count] - 5)];
}
// and, finally, pass on to the MKMapView
[mapView addAnnotations:allPotentialAnnotations];
probably the first thing to do is clustering your data on the map. that means combining several points to one point on the map. the more the user zoomes in the more the clusters will be resolved to single points placed on the map.
there are some recommendations how to do that in this post:
How can I reduce the number of annotations on a map?
the next step is to itterate through your data and check if one point is within the region arround the user location. you can get the GEO position of the visible display area like this:
CLLocationCoordinate2d topLeft, bottomRight;
topLeft = [mapView convertPoint:CGPointMake(0, 0) toCoordinateFromView:mapView];
CGPoint pointBottomRight = CGPointMake(mapView.frame.size.width, mapView.frame.size.height);
bottomRight = [mapView convertPoint:pointBottomRight toCoordinateFromView:mapView];

Wanted: How to reliably, consistently select an MKMapView annotation

After calling MKMapView's setCenterCoordinate:animated: method (without animation), I'd like to call selectAnnotation:animated: (with animation) so that the annotation pops out from the newly-centered pushpin.
For now, I simply watch for mapViewDidFinishLoadingMap: and then select the annotation. However, this is problematic. For instance, this method isn't called when there's no need to load additional map data. In those cases, my annotation isn't selected. :(
Very well. I could call this immediately after setting the center coordinate instead. Ahh, but in that case it's possible that there is map data to load (but it hasn't finished loading yet). I'd risk calling it too soon, with the animation becoming spotty at best.
Thus, if I understand correctly, it's not a matter of knowing if my coordinate is visible, since it's possible to stray almost a screenful of distance and have to load new map data. Rather, it's a matter of knowing if new map data needs to be loaded, and then acting accordingly.
Any ideas on how to accomplish this, or how to otherwise (reliably) select an annotation after re-centering the map view on the coordinate where that annotation lives?
Clues appreciated - thanks!
I ran into the same problem, but found what seems like a reliable and reasonable solution:
Implement the delegate method mapView:didAddAnnotationViews:. When I tried selecting the annotation directly within the delegate method, the callout dropped with the pin! That looked odd, so I add a slight delay of a half-second.
-(void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views {
[self performSelector:#selector(selectInitialAnnotation)
withObject:nil afterDelay:0.5];
}
Select the initial annotation as you'd expect, but calling selectAnnotation:animated;
-(void)selectInitialAnnotation {
[self.mapView selectAnnotation:self.initialAnnotation animated:YES];
}
It seems that selectAnnotation:animated: is not called under some conditions. Compare with MKMapView docs:
If the specified annotation is not onscreen, and therefore does not
have an associated annotation view, this method has no effect.
A more consistent way than using a fixed timeout is to listen to the regionDidChange callback. Set the center coordinate of the map to the desired annotation, and when the regionDidChange method is called, then select the annotation in the center, to open the callout.
Here's a little video I took of the thing running randomly between 5 pins.
First, goto the center coordinate of the annotation. Let's say the annotation object is named thePin.
- (void)someMethod {
[map setCenterCoordinate:thePin.coordinate animated:YES];
}
Then in the regionDidChange method, select this annotation.
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
[map selectAnnotation:thePin animated:YES];
}
Well just FYI, this is what the docs say,
If the specified annotation is not onscreen, and therefore does not
have an associated annotation view, this method has no effect.
So if I want to call setCenterCoordinate:animated: or setRegion:animated: and then I want to select the annotation by calling, selectAnnotation:animated: , the annotation won't get selected and the callout won't appear beacause of the exact same reason mentioned above in docs, So the way it would be great to have something like, setCenterCoordinate:animated:ComletionBlock but its not there..! The way that worked for me is as below,
[UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionBeginFromCurrentState|UIViewAnimationOptionCurveEaseInOut animations:^{
[self.mapView setCenterCoordinate:location.coordinate animated:YES];
} completion:^(BOOL finished) {
[self.mapView selectAnnotation:location animated:YES];
}];
This will give you a completion block and u can use that to select the annotation.
What has worked for me was calling selectAnnotation:animated: from the mapView:didAddAnnotationViews: method:
- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views;
{
[mapView selectAnnotation:[[mapView annotations] lastObject] animated:YES];
}
Apple documentation on the same here.
Note that I only had one annotation on the map so [[mapView annotations] lastObject] was fine for my purposes. Your mileage may vary.
I was having a similar problem. I was using the default MKPinAnnotationView with animatesDrop:YES. After I added the annotations to the MKMapView, I was doing this:
[mapView selectAnnotation:[mapView.annotations objectAtIndex:1] animated:YES]
which in the logic of my program, should select the nearest annotation. This wasn't working. I figured out the reason: the annotation view was not on the screen at the time of this select call, because of the pin drop animation. So all I did was set a timer to select the annotation a second later. It's a hack, but it works. I'm not sure if it'll work in every situation though, for instance on a 3G vs. 3Gs. It'd be better to figure out the right callback function to put it in.
selectTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self
selector:#selector(selectClosestAnnotation) userInfo:nil
repeats:NO];
- (void)selectClosestAnnotation {
[mapView selectAnnotation:[mapView.annotations objectAtIndex:1]
animated:YES];
}
I've found several problems with all the solutions I saw for my problem (I want to select a annotation when I added it from viewDidAppear):
Using the mapView:didAddAnnotationViews: delegate method.
This didn't work for me because if the specified annotation is not onscreen, and therefore does not have an associated annotation view, this method has no effect.
Using the mapViewDidFinishLoadingMap: delegate method.
This didn't work for me either because the map is cached now, so the method is called only once, the first time.
Solution:
John Blackburn was very close but without the mapView:didAddAnnotationViews: method. I just call my own selectAnnotation: method, before I add the annotation, with a delay:
[self.mapView addAnnotation:ann];
[self performSelector:#selector(selectAnnotation:) withObject:ann afterDelay:0.5];
And this is what I do in my selectAnnotation: method:
- (void)selectAnnotation:(id < MKAnnotation >)annotation
{
[self.mapView selectAnnotation:annotation animated:YES];
}
Tested on two iPhone 3GS (iOS 5 and 6) and an iPhone 5.
I was having similar difficulty, but was actually not able to make this method work at all, let alone inconsistently.
Here is what I found to work. Maybe it will work for you too:
How to trigger MKAnnotationView's callout view without touching the pin?

Resources