MKMapView and CLLocationManager - iphone

I want to use a MKMapView to display the user current location using the default breathing blue pin and I want to record the user movement at the same time. Is there any way that I could use the GPS manager (not sure if this is a CLLocationManager) the MKMapView uses when we enabled it to show user location?
I know that I can create my own CLLocationManager. But this feels like adding an overhead to my application and I would like the map and my tracking to remain in sync.
I already explored the following ideas without success:
Use the [MKMapView showUserLocation:YES] and add KVO on the userLocation field. This does not work and I am wondering if this is due to the fact that the userLocation field is read only.
Use the [MKMapView showUserLocation:YES], create a MKMapViewDelegate and add the tracking when the annotation view for the user location is requested. This does not work, because the annotation view is apparently requested only once???
Use a CLLocationManager and try to add the blue pin manually. Unfortunately, I did not find the blue pin in the available pin types, so I tried to create a user annotation manually without success.
Does anyone has any idea how I can achieve this and still benefit from the blue pin or is my only solution to use a CLLocationManager and create my own pin?

CLLocationManager uses the same data across all of its instances. MKMapView uses CLLocationManager's data internally. That said the solution to do what you want to do is let MKMapView do its own thing with regards to showUserLocation:. At the same time, create an instance of CLLocationManager and its delegate.
The delegate messages will give you the GPS coordinate location of MKMapView's blue pin. Everything will be in sync with each other.

Related

Stopping user location updates (the blue dot) for MKMapview

I am using an instance of CLLocationManager as well as a MKMapView for my app. I have a button that on tapped, will trigger startUpdatingLocation of my Location Manager which allows me to receive new location updates via the didUpdateToLocation callback. I stop these updates once I am satisfied with the accuracy:
[locationManager stopUpdatingLocation];
locationManager.delegate = nil;
The problem is, the GPS remains turned on as indicated by the arrow sign on the top right of the status bar. After further investigation, I realized that this is due to the fact that the mapView has showsUserLocation = YES, which means it will continue to receive updates, even if my own instance of locationManager has stopped the updating.
Adding the following after my stopping of the updates turns off the GPS sign, but the blue dot obviously disappears (but I do want the current position there still for visualization).
mapView.showsUserLocation = NO;
For my app, I need to use both CLLocationManager (to compute nearest POIs within a certain distance) and MKMapView (mainly for the display of these POIs as annotations around my locatiom). Is there a way to simply stop all GPS updates to both, and just freeze the blue dot at that point?
I thought of disabling mapView.showsUserLocation totally, and drawing the CLLocation from the LocationManager to the mapView as a custom annotation, but I am not sure that is the best way. Any ideas?
I think that once you are done updating location to the desired accuracy, dropping an annotation pin would be visual enough for the user to know the location selected. That is pretty standard among MKMapView usages.
Another thing to think about, the location arrow actually persists for a period of time (depends on the kind of usage), even after you stop monitoring location. You can find how long it persists in the Location Guidelines from Apple, but just something to consider as well.

iPhone - Updating Annotation subtitle in mapkit

I have a custom placemark with title and subtitle. The subtitle is actually displaying the address of the dropped pin using the reverse geocoder.
I have a button which has an action to drop the pin. This action gets the location coordinates of the user, and then calls [geocoder start] which gets the full address with Reverse Geocoder and generates the custom annotation and then calls [mapView addAnnotation:customPlacemark].
My problem is that using this sequence order, when there's no a WiFi connection (only 3G or maybe Edge) the pin takes a lot to drop because it's watigin to get the reverse geocoding info.
So basically I need to drop the pin without a subtitle and from the viewDidAnnotation call the geocoder and inside the reverseGeocoder update the subtitle but I'm not sure how to do that.
I want to display the annotation without the address details and update it when it gets the information from the reverse geocoder.
Any suggestions?
Thanks in advance
MKMapView observes changes its annotations via KVO. Therefore if you update your annotation's properties in a KVO compliant manner, it should Just Work.
For example, when the reverse geocoder returns an address for your annotation, you first announce the title and subtitle properties are about to change:
[self willChangeValueForKey:#"title"];
[self willChangeValueForKey:#"subtitle"];
Note that the above code is assumed to be in the annotation class.
Then update the annotation with information from the geocoder. When you are done:
[self didChangeValueForKey:#"subtitle"];
[self didChangeValueForKey:#"title"];
Note the order changed for didChangeValueForKey: as these need to be nested properly, somewhat like HTML tags.
This also works for the coordinate property, that will cause the pin to move.
I'd place the annotation, keep a reference to it in a property, then when your reverse geocoder calls back use the reference to the annotation and update its properties.

Why is the MKMapView's userLocation property rubbish ... for a while?

I have a Map View defined in IB and it is set to show the user location.
In my app, in -viewDidAppear, I query self.mapView.userLocation.location.coordinate and it comes back with insane values such as:
latitude: 4.8194501961644877e-49
longitude: 2.2993313035571993e-59
However, the next time -viewDidAppear is called (after I've simply moved to another tabbed view and then back to this one) the userLocation property holds exactly the correct values for my current location.
It seems that at the time of my initial call, the userLocation property has not been initialised but despite having read Apple's documentation I can't see any caveats where it says that this property is only valid after doing xxx.
Is there something that has to happen before userLocation is valid to use or should I just use CLLocationManager and ask it instead?
Thanks in advance for any help.
Sadly, Thomas' suggestion didn't help. What I have since discovered is:
If showsUserLocation is NO, then userLocation is never set correctly and -MapView:didUpdateUserLocation: is never called, consequently I never ever get a sensible location value.
So, to get the user's location I have to set showsUserLocation to YES, however that then means that after all my annotations have been added to the view (without including the user's location) I then calculate the required span to encompass them all and display them all at the right zoom level. After I do that though, the view jumps sideways as the Map View then automatically displays the user's location as the blue blob! As it was never included in the annotations to work out the zoom level I can't incorporate it into my calculations. Aaargh!
Note that when showsUserLocation is YES, then -MapView:didUpdateUserLocation: is called, but only after I've calculated all the coordinates of my annotations, not before!
I'm assuming it hasn't finished finding the user location - it has to work this out and it may take a while.
Instead of using it in viewDidLoad use THIS delegate method:
- (void)mapView:(MKMapView *)myMapView didUpdateUserLocation:(MKUserLocation *)userLocation;
You will need to set your mapview delegate to self. :)
Same is often true of Core Location. You'll get the last location lingering it its buffer, sometimes, or a super-broad throw-the-dart-at-the-map kind of location...
Best bet is to check the .horizontalAccuracy property of the location object and toss any that are too vague. It's good practice to just chuck the first one too.
for didUpdateUserLocation to be called you have to have...
mapView.showsUserLocation = TRUE;

When/where to startUpdatingLocation?

The determination of a position takes some time. When and where should the location manager be started?
Now I'm starting the location update one view before the result view (which needs the location) is loaded. If the user taps to fast I get 0.0 coordinates.
To get the right timing the startUpdatingLocation should be called three views before the result view. The problem here is I would have to pass the value trough this three view controllers.
The next thing is that I need the location also in another view (not the views mentioned above). I thought I will create an own location listener class. But when will this class be instantiated and the coordinates (longitude, latitude) passed through? A protocol doesn't help me, because only the class which created my location listener will get the results back.
I read a bit and come up with the following possible solutions:
1) Use of the Notification Center (see this solution)
2) Create a property of my location manager in the AppDelegate
At 1) I would have the problem that the view, which needs the results wouldn't have be created before.
How would the implementation of 2) would look like? I create an instance of the location manager in the app delegate and then I access some ivars like latitude/longitude of the AppDelegate? If I would take the solution described here I would have to implement the CLLocationManagerDelegate every time. Wouldn't it be better to have one class for doing that?
Perhaps you have a third solution?
A third solution may be to use a singleton class where you store the latitude and longitude of the location in a CLLocationCoordinate2D. You can then use the following code to access the stored coordinate from any view in your project
DataController* dataController = [DataController sharedDataController];
CLLocationCoordinate2D currentLocationCoordinate = dataController.coordinate;

Problem in moving MKPinAnnotation on iPhone map

I am building an app that tracks user location on map. I can insert a pin easily, but when the location is changed, the app quits.
I have set the default coordinates to 0,0 in viewdidload: method and I have added a pin at that location. I have done this because I want to remove the pin when location is updated and then insert the pin again on new location.
Here is the code which I have written in -(void)locationManager: didUpdateToLocation:fromLocation: method.
[mapview removeAnnotation:myannotation];
CLLocationCoordinate2D currentlocation;
currentlocation.latitude=newLocation.coordinate.latitude;
currentlocation.longitude=newLocation.coordinate.longitude;
myannotation=[[[CSMapAnnotation alloc]initWithCoordinate:currentlocation annotationType:CSMapAnnotationTypeStart title:#"My Location"] autorelease];
[mapview addAnnotation:myannotation];
Here, myannotation is the pin which I want to add,newLocation is the updated location and CSMapAnnotation will return annotation view.
The problem is my app crashes everytime. I was able to get warning message on console before crashing. Here is the message:
"An instance 0x182020 of class CSMapAnnotation is being deallocated while key value observers are still registered with it. Observation info is being leaked, and may even become mistakenly attached to some other object."
So, plz help if anyone has faced the same problem...
A few things to look out for:
MKMapView already has a showsUserLocation attribute that tracks the user's location for you with the pulsing blue-dot thing. If you want to separately set a pin on that location, you can get the userLocation attribute from the mapview.
The crashing bug could be because of the autorelease call. Easiest way to fix it is to make myannotation a property with a retain attribute, then take out the autorelease and instead of myannotation use self.myannotation.
Not really clear what you mean by setting "default coordinates to 0, 0." If these are lat/longs then you're putting a pin in Greenwich, England :-) You don't really need to continually add and remove annotations. You can add them once then adjust their position as needed.
I am thinking it is not liking your autorelease tag for myannotation, when you add the annotation to the map it might not be being retained, and then it just deallocates and crash, thats what i can think of from looking at the code you posted. Hope it helps
I just experienced this same problem and it had something to do with the coordinates of the annotation. I was already ignoring 0,0 but for some reason users were inputting -180,-180 (maybe from a device where we couldnt determine the location?)
anyways - as soon as I started ignoring -180,-180 the problem went away.
hope this helps.