Possible bug in MKMapView - iphone

If I create a ViewController with a map view and this is the only code I add to viewDidLoad:
MKPointAnnotation* annotation = [[MKPointAnnotation alloc] init];
annotation.coordinate = CLLocationCoordinate2DMake(-90, -180);
[self.mapView addAnnotation:annotation];
[self.mapView removeAnnotation:annotation];
[annotation release];
I get the error:
An instance 0xa126fa0 of class MKPointAnnotation was deallocated while key value observers were still registered with it. Observation info was leaked, and may even become mistakenly attached to some other object. Set a breakpoint on NSKVODeallocateBreak to stop here in the debugger. Here's the current observation info:
<NSKeyValueObservationInfo 0xa127df0> (
<NSKeyValueObservance 0xa127c90: Observer: 0xa11c530, Key path: coordinate, Options: <New: NO, Old: NO, Prior: YES> Context: 0x0, Property: 0xa127640>
If I change the code to this then I don't get any errors:
MKPointAnnotation* annotation = [[MKPointAnnotation alloc] init];
annotation.coordinate = CLLocationCoordinate2DMake(0, 0);
[self.mapView addAnnotation:annotation];
[self.mapView removeAnnotation:annotation];
[annotation release];
The only difference is that (0,0) is visible in the map, where as, (-90, -180) is out of view. That is, I need to pan the map to bring coordinate (-90, -180) into view.
Anyone experienced this error before or even better know how to fix it?

After some more testing I'm convinced it is a bug in MKMapView. I worked around it by only adding annotations that are in the visible region. More work but at least it doesn't crash my app :)

Related

Smooth Movement of annotation image on Mapview

I have loaded the custom annotation image for user current location.I am updating the current user location after every 1 sec in background.
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[self performSelectorOnMainThread:#selector(tempupdate) withObject:nil waitUntilDone:NO];
[pool release];
-(void)tempupdate
{
NSLog(#"callToLocationManager");
mylocationManager = [[CLLocationManager alloc]init];
NSLog(#"locationManagerM = %#",mylocationManager);
mylocationManager.delegate = self;
mylocationManager.desiredAccuracy = kCLLocationAccuracyBest;
mylocationManager.distanceFilter = 500;
[mylocationManager startUpdatingLocation];
}
After updating the current latutude and longitude i am refreshing the map using following code
MKCoordinateRegion region;
MKCoordinateSpan span;
span.latitudeDelta=0.002;
span.longitudeDelta=0.002;
CLLocationCoordinate2D location;
location.latitude=[lat doubleValue];
location.longitude=[longt doubleValue];
region.span=span;
region.center=location;
addAnnotation=[[AddressAnnotation alloc]initWithCoordinate:location];
addAnnotation.mTitle=#"You are here";
[self.mapView removeAnnotations:self.mapView.annotations];
[self.mapView addAnnotation:addAnnotation];
[self.mapView setRegion:region animated:TRUE];
[self.mapView regionThatFits:region];
But every time custom annotation image blinks before adding to the map.How to avoid this flicker effect?
I haven't tested this yet, but based on a quick glance at your code, I would guess the issue lies in your removal of the annotation and then adding a new one.
Have you tried just editing the already attached annotation's properties?
NSArray* curAnnotations = [mapView annotations];
AddressAnnotation* aa = [curAnnotations lastObject]; // I am assuming you only have 1 annotation
aa.mTitle = #"You are here"; // or don't do this if it never changes
[aa setCoordinate:location];
Note: Apple Docs specifically call out the 'setCoordinate' method as something that should be used to support dragging or frequent updates.
you mustn't remove annotation from map. Instead change the annotation coordinate in the UIView beginAnimations-commitAnimations block;
It will look something like this:
[UIView beginAnimations:#"yourAnimationName" context:nil];
[UIView setAnimationDuration:1.0];
[yourAnnotation setCoordinate:yourNewCoordinate];
[UIView commitAnimations];
It will move smoothly the annotation.
BR,
Marcin Szulc
//SWIFT 3
UIView.beginAnimations("yourname", context: nil)
UIView.setAnimationDuration(1.0)
yourpin.coordinate = MKCoordinateForMapPoint(mycoordinate)
UIView.commitAnimations()

Why didn't my map annotation move?

I am implementing a mapView whereby an annotation will be placed when the user search for an address. But somehow, the annotation sometime doesn't move and update to the new coordinate. It's only upon zooming the map, then will it update to the new location. The subtitle did get updated though.
- (void)searchBarSearchButtonClicked:(UISearchBar *)theSearchBar {
SVGeocoder *geocodeRequest = [[SVGeocoder alloc] initWithAddress:searchBar.text inRegion:#"sg"];
[geocodeRequest setDelegate:self];
[geocodeRequest startAsynchronous];
}
- (void)geocoder:(SVGeocoder *)geocoder didFindPlacemark:(SVPlacemark *)placemark {
if (annotation) {
[annotation moveAnnotation:placemark.coordinate];
annotation.subtitle = [NSString
stringWithFormat:#"%#", placemark.formattedAddress];
}
else {
annotation = [[MyAnnotation alloc]
initWithCoordinate:placemark.coordinate
title:#"Tap arrow to use address"
subtitle:[NSString
stringWithFormat:#"%#", placemark.formattedAddress]];
[mapView addAnnotation:annotation];
}
MKCoordinateSpan span;
span.latitudeDelta = .001;
span.longitudeDelta = .001;
MKCoordinateRegion region;
region.center = placemark.coordinate;
region.span = span;
[mapView setRegion:region animated:TRUE];
[searchBar resignFirstResponder];
}
I don't think MKMapView gets notified of changes to an annotation's location. The documentation of MKAnnotation's setCoordinate: says: "Annotations that support dragging should implement this method to update the position of the annotation." so it seems that's the only purpose of this method is for supporting dragging of pins.
Try removing the annotation from the map view before changing its coordinates and then add it back to the map view.
Nothing in your code (that you've shown) tells mapView that the annotation's location has changed. The annotation itself probably can't do it in -moveAnnotation because annotations generally don't know what map or maps they've been added to (nor should they).
The right way to move an annotation is to to remove it from the MKMapView that's using it, update its location, and then add it back to the map. You can't just change the annotation's location after it has been added to the map because the map may very well cache the location or sort the annotation according to its location, and there's no method in MKMapView to tell the map that the location has changed.
I'd change your conditional to something like this:
if (annotation == nil) {
annotation = [[MyAnnotation alloc] init];
annotation.title = #"Tap arrow to use address";
}
[mapView removeAnnotation:annotation];
[annotation moveAnnotation:placemark.coordinate];
annotation.subtitle = placemark.formattedAddress;
[mapView addAnnotation:annotation];
This assumes that it's safe to call -init in place of -initWithCoordinate:title:subtitle:; if not, you'll want to change that.

MKMapView Annotation setCoordinate crashing app

I am working on a simple GPS-like app. I created a custom object which extends MKAnnotation which also works fine. I can place it etc, but if I put this piece of code [mpm setCoordinate:loc]; in my code, the app opens and freezes while showing the basic gray-grid (not showing any downloaded map and non of my buttons work either then)
Here is my header:
#interface FirstViewController : UIViewController <MKMapViewDelegate>
{
IBOutlet MKMapView *mapView;
MyPlaceMark *mpm;
}
-(void)locationUpdate:(CLLocation *)location;
-(void)locationError:(NSError *)error;
#end
And here is the piece of code where I try to update things:
-(void)locationUpdate:(CLLocation *)location
{
CLLocationCoordinate2D loc = [location coordinate];
[mpm setCoordinate:loc]; // This line messes it up.
[mapView setCenterCoordinate:loc];
if([mapView showsUserLocation] == NO)
[mapView setShowsUserLocation:YES]; // This does not show my position either?
}
If I comment that line out, the app works fine. I need to update the annotation as it will be my marker for showing the users current location. PS: Without that line, it does center my view - so location is a valid/set variable.
My viewDidLoad looks like this:
- (void)viewDidLoad {
mapView = [[MKMapView alloc] initWithFrame:self.view.bounds];
//mapView.showsUserLocation = YES;
[mapView setShowsUserLocation:YES];
mapView.mapType = MKMapTypeStandard;
mapView.delegate = self;
CLLocationCoordinate2D location;
MKCoordinateRegion region;
location.latitude = -33.8;
location.longitude = 18.6;
MKCoordinateSpan span;
span.latitudeDelta = 0.01;
span.longitudeDelta = 0.01;
region.span = span;
region.center = location;
[mapView setRegion:region animated:TRUE];
[mapView regionThatFits:region];
mpm = [[MyPlaceMark alloc] initWithCoordinate:location]; // Creating mpm
[mapView addAnnotation:mpm]; // Adding mpm
[self.view addSubview:mapView];
[super viewDidLoad];
}
Should I re-add mpm after I changed it's location? Or would it just jump to its new location on the map?
So to recap the question: How can I update the custom MKAnnotation's location on my mapview?
Thank you for your time!
EDIT: I think the main reason for the crash is the fact that I do not create a setCoordinate method/function in my custom MKAnnotation. How would I override that but keep it same as original?
The error in log-console:
2011-06-09 14:42:40.377 AeroNav[3706:707] -[MyPlaceMark setCoordinate:]: unrecognized selector sent to instance 0x19e0e0
2011-06-09 14:42:40.493 AeroNav[3706:707] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[MyPlaceMark setCoordinate:]: unrecognized selector sent to instance 0x19e0e0'
...
terminate called after throwing an instance of 'NSException'
[mapView setShowsUserLocation:YES] doesn't update the region that the map shows. You will have to implement the delegate methods didUpdateUserLocation: and call the setRegion:animated: there to move the region to user's location. Until this is called for the first time, the userLocation property of MKMapView is not set.
As for the MKAnnotation thing, MKAnnotation is a protocol. It doesn't have a default implementation. You will have to provide for all the methods and properties that you've agreed to conform to. So declare a property pretty similar to the one in the MKAnnotation protocol.

Why is MKMapView putting me in the sea?

I have this code to set the map at the user's location:
MKCoordinateSpan span;
span.latitudeDelta=0.2;
span.longitudeDelta=0.2;
CLLocationCoordinate2D location=mapView.userLocation.coordinate;
location = mapView.userLocation.location.coordinate;
MKCoordinateRegion region;
region.span=span;
region.center=location;
[mapView setRegion:region animated:TRUE];
[mapView regionThatFits:region];
Now when I do mapView.showsUserLocation=YES it shows my location at the right place (on my device, and the apple hq in the simulator) however, both times the code above puts me in the sea somewhere near africa!
Any ideas?
The latitude/longitude pair 0/0 is on the equator due south of Greenwich, England. That spot is in the Gulf of Guinea, off the West coast of Africa. If that's where your pin is getting placed, then your mapView.userLocation isn't getting set.
Probably your code here is running before MKMapKit has had a chance to get its location. What you really want to do is have your viewController adopt the MKMapKitDelegate protocol, set the .delegate property of your map view to self, and then implement the method:
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
That method will get called when the map kit locates the user, and you can use the userLocation value you get passed there to set the region on the map. Until that gets called the first time, though, the MapView is still working on locating itself, and your asking about its location is premature.
Also, your regionThatFits there at the end is a no-op. That method doesn't resize the region in place, it actually returns a resized region, for you to use however you will. So you want to set your map view's region to what that method returns. Like this:
[mapView setRegion:[mapView regionThatFits:region] animated:TRUE];
What I have done was add the following entry on info.plist:
NSLocationAlwaysUsageDescription
Got this info into this (very good) tutorial:
https://www.youtube.com/watch?v=Z272SMC9zuQ
You can choose this method..
- (void)mapView:(MKMapView *)_mapView regionDidChangeAnimated:(BOOL)animated{
CLGeocoder *ceo = [[CLGeocoder alloc]init];
CLLocation *loc = [[CLLocation alloc]initWithLatitude:_mapView.region.center.latitude longitude:_mapView.region.center.longitude];
CLLocationAccuracy accuracy = _mapView.userLocation.location.horizontalAccuracy;
if (accuracy) {
current_pickup_location = loc;
[_currentStreetSpinner startAnimating];
[ceo reverseGeocodeLocation:loc
completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error){
CLPlacemark *placemark = [placemarks objectAtIndex:0];
NSString *locatedAt = [[placemark.addressDictionary valueForKey:#"FormattedAddressLines"] componentsJoinedByString:#", "];
// NSLog(#"location %#",placemark.location);
NSLog(#"I am currently at %#",locatedAt);
_currentStreetLabel.text = locatedAt;
static_iVar = _currentStreetLabel.text;
[_currentStreetSpinner stopAnimating];
[self listDoctorsWithSpeciality_id:currentSpeciality_id
withProfessionalityId:currentProfessionality_id];
}];
}
}
I am answering this question because the answer picked did not really help me and I was having the exact same issue. didUpdateUserLocation is not very practical because it makes the app update every single time the location changes. The other issue with the above solution is that when the map loads it's still too early to ask for a location. So how do you ask for user location before the the map loads so that the zooming doesn't take you to Africa?
You need to request the location by using the location manager on the viewDidLoad. Then after that you can set the region.
SWIFT 3
//top of your class
let locManger = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
//request user location
locManager.startUpdatingLocation()
//this is the code that sets the region based on the location
let span = MKCoordinateSpanMake(0.5, 0.5)
let location = CLLocationCoordinate2DMake((locManager.location?.coordinate.latitude)!, (locManager.location?.coordinate.longitude)!)
let region = MKCoordinateRegionMake(location, span)
yourMap.setRegion(region, animated: false)
//stop location updating
locManager.stopUpdatingLocation()
}

userLocation: latitude/longitude return zero?

I am just looking at mapKit and decided to make a quick button to display my current location, however when I press the button my latitude/longitude always display as [0.000000] [0.000000].
The mapView is loaded as I can see the map on the simulator before I press the button. Previously I have done this by using coreLocation.framework and using CLLocationManager and asking for the device location that way. I am just curious why this way is not working correctly, would I be better doing this via CLLocationManager?
-(IBAction)findMePressed {
MKUserLocation *myLocation = [myMapView userLocation];
CLLocationCoordinate2D coord = [[myLocation location] coordinate];
[myMapView setCenterCoordinate:coord animated:YES];
NSLog(#"findMePressed ...[%f][%f]", coord.latitude, coord.longitude);
}
EDIT: Added ...
-(IBAction)findMePressed {
MKUserLocation *myLocation = [myMapView userLocation];
CLLocationCoordinate2D coord = [[myLocation location] coordinate];
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(coord, 350, 350);
[myMapView setRegion:region animated:YES];
}
-(void)viewDidLoad {
[super viewDidLoad];
[myMapView setShowsUserLocation:YES];
}
Gary.
Either the userLocation is not visible on the map (see the userLocationVisible property) or there is some problem setting up the myMapView property and it's nil (i.e. not connected in interface builder)
[...] as I can see the map on the simulator [...]
Test it on the device. By default, on the simulator, the coordinates you get back are Apple's headquarters. Cf. doc.
See this other SO question for workarounds and useful utilities : Testing CoreLocation on iPhone Simulator