MKMapView annotation position update problem - iphone

I need to track user current location with realtime refreshrate
I have one function with two solutions for that.
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
# ifdef Variant_1
if(m_currentLocation)
[m_Map removeAnnotation:m_currentLocation];
else
m_currentLocation = [MKPlacemark alloc];
[m_currentLocation initWithCoordinate:newLocation.coordinate addressDictionary:nil];
[m_Map addAnnotation:m_currentLocation];
[m_Map setCenterCoordinate:m_currentLocation.coordinate animated:YES];
# else //Variant_2
if(m_currentLocation == nil)
{
m_currentLocation = [MKPlacemark alloc];
[m_currentLocation initWithCoordinate:newLocation.coordinate addressDictionary:nil];
[m_Map addAnnotation:m_currentLocation];
}else
{
[m_currentLocation initWithCoordinate:newLocation.coordinate addressDictionary:nil];
//[m_currentLocation setCoordinate:newLocation.coordinate];
}
[m_Map setCenterCoordinate:m_currentLocation.coordinate animated:YES];
# endif
}
Variant_1 works good but when you move fast the location sing on the map blinks.
Variant_2 does not blink but does not move location sing however moves map.
Where is the problem?

In Variant_1, it probably blinks because you're doing a removeAnnotation and then an addAnnotation instead of just modifying the coordinates of the existing annotation.
In Variant_2, initWithCoordinate returns a new MKPlacemark object with those coordinates. It doesn't update the properties of the object you are calling the method on.
What happens if you run the setCoordinate line instead?
A separate question is why not use the MKMapView's built-in ability to show the current user location? Just do m_Map.showsUserLocation = YES; at the start. You don't need CLLocationManager to get the user's current location if you are using the MKMapView anyway.
I think you'll still need to center the map on the user's current location using one of the map view delegate methods:
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
[mapView setCenterCoordinate:userLocation.location.coordinate animated:YES];
}

Related

Center maps in iOS Programming

How do we follow the user in maps. I want to have the blue dot (user location) be in the center of the map, But I also what to allow the user to zoom in and zoom out and then after a couple seconds zoom in back in the user location.
My Educated Guess for the Solution: We detect if the user is zooming in or out, after three seconds of no zooming in or out detection, we starting follow the user :). Your HELP would be awesome :)
This code zoom in the user location but doesn't delay for zoom in and out:
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation {
MKCoordinateRegion userLocation = MKCoordinateRegionMakeWithDistance(newLocation.coordinate, 1500.0, 1500.0); [mapView setRegion:userLocation animated:YES];
}
A quick look in the docs reveals the magic.
Set the userTrackingMode of your map to MKUserTrackingModeFollow.
See here.
Update:
Since you've updated your question, here's the new answer.
To recenter the map to the user location i would recommend to write a simple helper Method:
- (void)recenterUserLocation:(BOOL)animated{
MKCoordinateSpan zoomedSpan = MKCoordinateSpanMake(1000, 1000);
MKCoordinateRegion userRegion = MKCoordinateRegionMake(self.mapView.userLocation.coordinate, zoomedSpan);
[self.mapView setRegion:userRegion animated:animated];
}
And now you should call it after a short delay if user has stopped moving the map. You can do this in the regionDidChange delegate method of the mapView.
But you can get problems if you don't lock the reset-method if the user changes the region multiple times before it really resets the map. So it would be wise to make a flag if it is possible to recenter the map. Like a property BOOL canRecenter.
Init it with YES and update the recenterUserLocation method to:
- (void)recenterUserLocation:(BOOL)animated{
MKCoordinateSpan zoomedSpan = MKCoordinateSpanMake(1000, 1000);
MKCoordinateRegion userRegion = MKCoordinateRegionMake(self.mapView.userLocation.coordinate, zoomedSpan);
[self.mapView setRegion:userRegion animated:animated];
self.canRecenter = YES;
}
Now you can call it safely after the user has moved the map in any way with a small delay:
- (void)mapView:(MKMapView *)mMapView regionDidChangeAnimated:(BOOL)animated{
if (self.canRecenter){
self.canRecenter = NO;
[self performSelector:#selector(recenterUserLocation:) withObject:#(animated) afterDelay:3];
}
}
I had the same problem. I guessed:
If the user drag the map, he wants to stay on that position.
If the user do nothing or reset to show current location, I need to follow the user.
I added a reset button to show the current user location like this:
On the reset button clicked, changed the needToCenterMap to TRUE
Added a drag gesture recognizer on map
// Map drag handler
UIPanGestureRecognizer* panRec = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(didDragMap:)];
- (void)didDragMap:(UIGestureRecognizer*)gestureRecognizer {
if (gestureRecognizer.state == UIGestureRecognizerStateEnded){
NSLog(#"Map drag ended");
self.needToCenterMap = FALSE;
}
}
Followed the user on map depending on needToCenterMap flag
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
if (self.needToCenterMap == TRUE)
[mapView setCenterCoordinate:userLocation.location.coordinate animated:YES];
}
I made a little example to show how you can delegate this job to the Map SDK.
Of course you could listen to the Location change but MKUserTrackingModeFollow automatically does this for you, so just a single line of code
#import <MapKit/MapKit.h>
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
MKMapView *mapView = [[MKMapView alloc] initWithFrame:self.view.frame];
//Always center the dot and zoom in to an apropriate zoom level when position changes
[mapView setUserTrackingMode:MKUserTrackingModeFollow];
//don't let the user drag around the the map -> just zooming enabled
[mapView setScrollEnabled:NO];
[self.view addSubview:mapView];
}
Then the app looks like this:
For more information just read the Apple Documentation:
http://developer.apple.com/library/ios/#documentation/MapKit/Reference/MKMapView_Class/MKMapView/MKMapView.html
This shell do the trick: mkMapview.showsUserLocation = YES;

iOS Core Location Set Pin to user location in viewDidLoad

I am attempting to set a map annotation to the user's current location. I am trying to set the pin in the viewDidLoad method, however because the method
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
Has not been called yet, the lat and long are 0.000000. Is there a way to call this method in my viewDidLoad or any other solution that will make a pin appear at my beginning location when the application loads?
UPDATE, Added Annotation Code
CLLocationCoordinate2D theCoordinate;
theCoordinate.latitude = (_currentLocation.latitude);
theCoordinate.longitude = (_currentLocation.longitude);
NSLog(#"The Coordinate Value:");
NSLog(#"%f, %f",theCoordinate.latitude,theCoordinate.longitude);
DDAnnotation *annotation = [[[DDAnnotation alloc] initWithCoordinate:theCoordinate addressDictionary:nil] autorelease];
annotation.title = #"Drag to Move Pin";
annotation.subtitle = [NSString stringWithFormat:#"%f %f", annotation.coordinate.latitude, annotation.coordinate.longitude];
[self.mapView addAnnotation:annotation];
UPDATE 2
Still not working, code is in the didUpdateLocation Method
static BOOL annotationAdded = NO;
if (!annotationAdded) {
annotationAdded = YES;
CLLocationCoordinate2D theCoordinate;
theCoordinate.latitude = _currentLocation.latitude;
theCoordinate.longitude = _currentLocation.longitude;
//Sets Initial Point to Africa Because Method to obtain current Location
//Hasen't Fired when View Loads
theCoordinate.latitude = (mapView.userLocation.coordinate.latitude);
theCoordinate.longitude = (mapView.userLocation.coordinate.longitude);
NSLog(#"The Coordinate Value:");
NSLog(#"%f, %f",theCoordinate.latitude,theCoordinate.longitude);
DDAnnotation *annotation = [[[DDAnnotation alloc] initWithCoordinate:theCoordinate addressDictionary:nil] autorelease];
annotation.title = #"Drag to Move Pin";
annotation.subtitle = [NSString stringWithFormat:#"%f %f", annotation.coordinate.latitude, annotation.coordinate.longitude];
[self.mapView addAnnotation:annotation];
}
MKMapView automatically places an annotation of class MKUserLocation when you set mapView.showsUserLocation = YES.
You can replace the default view for this annotation to whatever default annotation view you want by doing this in mapView:viewForAnnotation::
- (MKAnnotationView *) mapView:(MKMapView *)theMapView viewForAnnotation:(id <MKAnnotation>)annotation {
if ([annotation isKindOfClass:[MKUserLocation class]]) {
// replace the following with code to generate and return custom user position annotation view
return customAnnotationView;
}
//*** other code ***//
}
Update:
If all you want to do is set a pin initially (once) at the user's location when the view loads, then you will have to wait until the phone can grab the data you need since that takes some time. Add your annotation in mapView:didUpdateUserLocation the first time it is called, and that should do the trick:
- (void) mapView:(MKMapView *)theMapView didUpdateUserLocation:(MKUserLocation *)userLocation {
static BOOL annotationAdded = NO;
if (!annotationAdded) {
annotationAdded = YES;
//*** add annotation to mapView ***//
}
}
Final Comment:
I would generally avoid setting a static pin at a users location the first time this method is called, however, and instead opt to just using the default standard blue dot. That is because the location services in the phone take time to find an accurate reading on the user's location, but in the interest of time it will send you location updates as soon as possible. This means that the first location update may not be very accurate, but subsequent updates may be much more accurate. That is why the standard blue dot sometimes changes position frequently within the first few moments of showing up on the map.
Just a caveat. Obviously what you choose to do depends on what the purpose of your app is.
I've never found a way to manually call that method. I believe it's a delegate method that's completely passive. Sorry.
It takes some time for the device to determine the location -- you can't speed up the process by calling -locationManager:didUpdateToLocation: yourself. You'll need to either use #Matt's suggestion to let the map draw the user's location, or else wait for -...didUpdateToLocation: to be called and take action then.

Placing a blue dot (user location) Only using CLLocationManager

I've read through countless posts here on stack and apple's docs and can't find anything to solve this problem.
The issue is if you set mapView.showsUserLocation = YES, then MapKit will start making it's own GPS queries to your phone.
From apple docs:
Setting this property to YES causes
the map view to use the Core Location
framework to find the current
location. As long as this property is
YES, the map view continues to track
the user’s location and update it
periodically.
If you also want to use CLLocationManager, then when you make a call to [mylocationmanager startUpdatingLocation], then you are making a second GPS query on your phone.
Now you have 2 separate processes asking for GPS location.
Not a problem on the simulator, but if you try it on a real phone it will take a very very long time to get the GPS location. It is also inconsistent 10seconds - 1 minute, whereas if you turn off mapView.showsUserLocation it takes 2-3 seconds very consistently.
In general it seems like a very bad practice to use both.
For flexibility and control, I'd rather use CLLocationManager, but if you don't set mapView.showsUserLocation = YES, then you don't get the blue dot!
I tried the usual overwrite annotation methods: eg:
- (MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>) annotation
{
if ([annotation isKindOfClass:MKUserLocation.class]) {
//it's the built-in user location annotation, return nil to get default blue dot...
return nil;
}
//handle your custom annotations...
}
But it doesn't work, most probably because there is never a call to actually place a user annotation on the map.
So does anyone have a solution to only use CLLocationManager to place the user's location on the map?
Just overriding the viewForAnnotation method isn't enough, you first have to add you annotations to the map by calling
[mapView addAnnotation:annotationObject];
Your annotationObject can be an instance of any class that implements the MKAnnotation protocol. You can find the details in the MapKit Guide in the Annotating apps section.
If you need to show just blue dot around your location (accuracy), you can do it like this:
MKCircle *accuracyCircle;
-(void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
// when you want update your position and accuracy
[self.mapView removeOverlay:accuracyCircle];
accuracyCircle = [MKCircle circleWithCenterCoordinate:newLocation.coordinate
radius:newLocation.horizontalAccuracy];
[self.mapView addOverlay:accuracyCircle];
}
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay
{
if([overlay isKindOfClass:[MKCircle class]])
{
MKCircleRenderer * circleRenderer = [[MKCircleRenderer alloc] initWithOverlay:overlay];
circleRenderer.fillColor = [UIColor colorWithRed:0 green:1 blue:0 alpha:0.2];
return circleRenderer;
}
return nil;
}

iPhone - User location not showing when updated with CLLocation manager on iOS 3.x

I have a mapview which when pushed with its view controller onto the screen shows the location of a
user and some other custom annotations. when the view controller is pushed to screen it calls a function -(void)adduserLocation which shows the location of the user.
I also have a refresh button which also calls -(void)adduserLocation and refreshes all of the annotations and the user location but when the refresh button is pressed the users location does not appear on the map even though all of the same callbacks and updates are being registered (I can see that the user location is received and all the necessary callbacks are made)
I am using :
-(MKAnnotationView *)mapView:(MKMapView *)mV viewForAnnotation:(id <MKAnnotation>)annotation {
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil; //for some reason this is only being returned when the view is pushed and the get
//location method is called but not when the same location method is called
//while the view already exists.
}
and:
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
NSLog(#"User location: %f, %f", newLocation.coordinate.latitude, newLocation.coordinate.longitude);
currentRegionLat = newLocation.coordinate.latitude;
currentRegionLng = newLocation.coordinate.longitude;
CLLocationCoordinate2D coord = {latitude: newLocation.coordinate.latitude, longitude: newLocation.coordinate.longitude};
MKCoordinateSpan span = {latitudeDelta: 0.006, longitudeDelta: 0.006};
MKCoordinateRegion region = {coord, span};
[mView setRegion:region];
[self getCardsInLocationLat:newLocation.coordinate.latitude andLng:newLocation.coordinate.longitude]; //this is a function which sets the annotations
}
I have no idea what I may be doing wrong. Please help. Thanks.
*UPDATE**
I have managed to find the source of the problem and work around it:
The problem was that I was removing all the annotations on the map before doing a location update
and for some reason the user location annotation was not being added.
So what I did is simply remove only all the non [MKUserLocation class] annotations and left
the user location annotation. This solved my problem but I still think that either there is some
kind of apple bug here or I am doing something wrong which I cannot yet see.
Hey, why don't you just set the MKMapView property, showsUserLocation, to YES?
mapView.showsUserLocation = YES;
This way you wouldn't really need to worry about refreshing the location.
If you don't want to use it for battery saving purposes or something, I think we would need to see more of your code. The code that you have doesn't seem to be where the problem lies. Do you get any errors in the console?

showsUserLocation does not display blue dot in iPhone 4.0

So, I've created a CLLocationManager, called it to start updating, set mapView.showsUserLocation to YES, and returned nil for the userLocation annotation.
Here are some snippets from my code in my UIMapViewController:
- (void)viewDidLoad
{
CLLocationManager *locationManager = [[CLLocationManager alloc] init];
[locationManager startUpdatingLocation];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
CLLocationCoordinate2D userCoordinate = locationManager.location.coordinate;
[map setCenterCoordinate:userCoordinate animated:YES];
[map setShowsUserLocation:YES];
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
MKAnnotationView *mapIconView = (MKAnnotationView *) [mapView dequeueReusableAnnotationViewWithIdentifier:#"mapIconView"];
// Don't mess with the user location annotation
if (annotation == mapView.userLocation)
return nil;
// etc.
}
This all seems pretty straightforward. Everything works fine--the map zooms to my location as it should, and I've confirmed that all the methods are called as expected--but no blue dot. Can't get the blue dot for the life of me, no matter where or how many times I say
mapView.setUserLocation = YES;
What am I missing here?
When I do this, rather than checking annotation like you have, I do something along the lines of:
if([annotation class] == MKUserLocation.class) {
return nil;
}
I had this same problem where the blue dot wouldn't appear. Turns out it was appearing, just not where I thought it was.
Similarly to my issue, your code looks like it's both processing location updates and asking the MKMapView to track the user location. Note that in the simulator, these are two different locations! When the MKMapView is tracking the user location in the simulator, it gives you the location of Apple in Cupertino, CA, regardless of your "actual" location.
Using this code and letting the MKMapView track the location for your delegate instead of tracking the location yourself can reveal the blue dot:
- (void)mapView:(MKMapView *)theMapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
[theMapView setCenterCoordinate:userLocation.location.coordinate animated:YES];
}
You'll probably want to zoom in a bit. I used the handy code from this blog entry to do that: Set the Zoom Level of an MKMapView
It's mapView.showUserLocation = YES;
You seem to have used the wrong statement 'set' instead of 'show'.
And FYI, you don't have to mess with the location manager just to get the userlocation. Corelocation automatically gets fired when you set mapview.showUserLoction = YES and load the map.
Hope it helps :)
Make sure that you are not removing all annotations from the map anywhere:
e.g. [mapView removeAnnotations:mapView.annotations]
If you are using the iOS simulator, then you can't see the blue dot. I had the exact same problem and when I started to try my software with the real hardware the blue dot showed up!