I am building an iOS application using google maps SDK. I can add some markers on the maps when user does a longPressAtCoordinate. My problem is that when I am trying to drag a marker the diiLongPressAtCoordinate is fired before the didBeginDraggingMarker so a new marker is added also.
-(void)mapView:(GMSMapView *)mapView didBeginDraggingMarker:(GMSMarker *)marker{
NSLog(#"begin dragging marker");
}
- (void)mapView:(GMSMapView *)mapView didLongPressAtCoordinate (CLLocationCoordinate2D)coordinate{
NSLog(#"did long press at mapview");
//when user didLongPressAtCoordinate I add a new marker on the map.
// I want to prevent the execution of this code before the didBeginDraggingMarker method
}
I solved this problem by creating a boolean property called isDragging and changing it's value depending whether a marker is being dragged.
- (void)mapView:(GMSMapView *)mapView didBeginDraggingMarker:(GMSMarker *)marker
{
self.isDragging = YES;
}
- (void)mapView:(GMSMapView *)mapView didEndDraggingMarker:(GMSMarker *)marker
{
self.isDragging = NO;
}
Then I validate if a marker is being dragged whenever a long press is detected:
- (void)mapView:(GMSMapView *)mapView didLongPressAtCoordinate:(CLLocationCoordinate2D)coordinate
{
if (self.isDragging) {
return;
}
NSLog(#"Long press detected");
}
Related
I have implemented the Google Maps in my iOS app and to show User Location, I am using custom Marker Icon. Everything is working properly. I have implemented the GMSMapViewDelegate.
The issue is that the event didTapAtCoordinate: is not getting fired when I tap on the Marker Icon, but it does when I tap anywhere on the Map. Just the marker is not tappable while I have tried to set it marker.tappable = YES;
I have searched over the internet but couldn't get that what I am doing wrong or missing.
Below is the code:
/** SETUP MAP & MARKERS **/
-(void) setupMapMarkers {
self.mapView.delegate = self;
/** SET CAMERA POSITION ON MAP **/
GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:[self.userObject.latitude doubleValue]
longitude:[self.userObject.longitude doubleValue]
zoom:10];
self.mapView.camera = camera;
/** ADDING USER'S LOCATION MARKER **/
CLLocationCoordinate2D position = CLLocationCoordinate2DMake([self.userObject.latitude doubleValue], [self.userObject.longitude doubleValue]);
GMSMarker *marker = [GMSMarker markerWithPosition:position];
marker.tappable = YES;
NSURL *url = [[NSBundle mainBundle] URLForResource:#"pin_user_active" withExtension:#"gif"];
marker.icon = [UIImage animatedImageWithAnimatedGIFData:[NSData dataWithContentsOfURL:url]];
marker.map = self.mapView;
}
#pragma mark - GMSMapViewDelegate
- (void)mapView:(GMSMapView *)mapView didTapAtCoordinate:(CLLocationCoordinate2D)coordinate {
NSLog(#"You tapped at %f,%f", coordinate.latitude, coordinate.longitude);
}
// Added it just to check, if it works on tap
- (void)mapView:(GMSMapView *)mapView didTapOverlay:(GMSOverlay *)overlay {
NSLog(#"tapped");
}
Give something to marker.title = #"some text" and call this delegate method:
- (void)mapView:(GMSMapView *)mapView didTapInfoWindowOfMarker:(GMSMarker *)marker;
When you click on the title, this method will be invoked.
After going through GOOGLE MAPS COCOA DOCS, got to know that there is another delegate method specifically for markers tap.
For delegate method
- (void)mapView:(GMSMapView *)mapView didTapAtCoordinate:(CLLocationCoordinate2D)coordinate
It states that:
Called after a tap gesture at a particular coordinate, but only if a marker was not tapped. This is called before deselecting any currently selected marker (the implicit action for tapping on the map).
So for markers tap event we need:
- (BOOL)mapView:(GMSMapView *)mapView didTapMarker:(GMSMarker *)marker
I hope it will help others too.
I am using MKMapView fro map. In one view controller I am getting lat long to drop pin in map. Now from preview button user can see that location with pin. Now I have one button by clicking on that button user can drag pin at their desire location. And app can get that lat long. How to implement this
Use this code and also adopt MKMapViewDelegate
- (void)mapView:(MKMapView *)mapView
annotationView:(MKAnnotationView *)annotationView
didChangeDragState:(MKAnnotationViewDragState)newState
fromOldState:(MKAnnotationViewDragState)oldState
{
if (newState == MKAnnotationViewDragStateEnding)
{
CLLocationCoordinate2D droppedAt = annotationView.annotation.coordinate;
NSLog(#"Pin dropped at %f,%f", droppedAt.latitude, droppedAt.longitude);
}
}
I am adding some functionality in
- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated
and
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
I want this to be called when the map region is changed.
How can i prevent these delegate methods from being called when device changes its orientation?
Add a property BOOL didRotate
Then add something like
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration{
self.didRotate = YES;
}
and
- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated{
if(self.didRotate) return;
}
and
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated{
if(self.didRotate) {
self.didRotate = NO;
return;
}
}
These delegate methods are called when the frame of the map view changes. The frame can change on rotation because of autolayout constraints or autoresizing masks. So one solution would be to keep the map view frame constant.
You could also try to unset the delegate while the interface orientation is changing.
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
_mapView.delegate = nil;
}
-(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
_mapView.delegate = self;
}
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've created a test application with only one view containing an MKMapView and a controller which acts as the MapView's delegate.
When I do a fresh build (removed from the device completely before re-installing) and log the callbacks, I can see that mapView:didUpdateUserLocation is called twice before the user has indicated whether they wish to show their current location or not.
The MKUserLocation objects passed to the callback are invalid:
2012-03-13 08:20:17.518 MapTest[3325:707] Did update user location: 0.000000,0.000000
2012-03-13 08:20:17.581 MapTest[3325:707] Did update user location: 0.000000,0.000000
Is this the expected behaviour for MKMapKit or a bug?
Update
I'm running this on my iPhone 4, not a simulator.
Here's the controller code:
#import "ViewController.h"
#implementation ViewController
#synthesize mapView;
- (void)viewDidLoad
{
[super viewDidLoad];
self.mapView.showsUserLocation = YES;
self.mapView.delegate = self;
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
-(IBAction)trackButtonTapped:(id)sender
{
self.mapView.showsUserLocation = !self.mapView.showsUserLocation;
}
#pragma mark - MKMapKitDelegate
-(void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
NSLog(#"Did update user location: %f,%f", userLocation.coordinate.latitude, userLocation.coordinate.longitude);
}
-(void)mapViewWillStartLoadingMap:(MKMapView *)mapView
{
NSLog(#"Will start loading map");
}
-(void)mapViewDidFinishLoadingMap:(MKMapView *)mapView
{
NSLog(#"Did finish loading map");
}
-(void)mapViewWillStartLocatingUser:(MKMapView *)mapView
{
NSLog(#"Will start locating user");
}
-(void)mapViewDidStopLocatingUser:(MKMapView *)mapView
{
NSLog(#"Did stop locating user");
}
-(void)mapViewDidFailLoadingMap:(MKMapView *)mapView withError:(NSError *)error
{
NSLog(#"Did fail loading map");
}
-(void)mapView:(MKMapView *)mapView didFailToLocateUserWithError:(NSError *)error
{
if (error.code == kCLErrorDenied){
NSLog(#"User refused location services");
} else {
NSLog(#"Did fail to locate user with error: %#", error.description);
}
}
#end
My opinion is that it's a bug.
How can the map view say user location has been updated when the user hasn't even granted permission?
The workaround I use is to check if userLocation.location is nil:
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
if (userLocation.location == nil)
return;
//do something with userLocation...
}
instead checking for nil userlocation (where 0,0 coordinates can actually be valid) you should use:
if([CLLocationManager authorizationStatus] == kCLAuthorizationStatusAuthorized) {
// do something with the coords
} else {
// the coords are invalid
}
With "self.mapView.showsUserLocation = YES;" in viewDidLoad it will start trying to get the user's location straight away. If you take that out it'll wait until the user has pressed a button.
Maybe this helps someone.
For me the "problem" was that I had set show user's location trough interface builder too. Removing it here and setting it through code made the delegate methods get work as suspected.