selectAnnotation method of Mapview calling deselect method also iOS - ios

I have been working on this problem for 6 hours and I am still struggling.
I have a mapview and I am adding MKPolygons like this:
for (MKPolygon *polygon in arrPolygon){
[mapView addOverlay:polygon];
[mapView addAnnotation:polygon];
}
I am finding which polygon overlay was tapped and select corresponding annotation programatically:
WildcardGestureRecognizer *tapges=[[WildcardGestureRecognizer alloc] init];
tapges.touchesBeganCallback = ^(NSSet * touches, UIEvent * event) {
UITouch *touch = [touches anyObject];
tappedOverlay = nil;
if([touch tapCount]==1){
for (id<MKOverlay> overlay in mapView.overlays)
{
MKOverlayView *view = [mapView viewForOverlay:overlay];
if ([overlay isKindOfClass:[MKPolygon class]] && view)
{
// Get view frame rect in the mapView's coordinate system
CGRect viewFrameInMapView = [view.superview convertRect:view.frame toView:mapView];
// Get touch point in the mapView's coordinate system
CGPoint point = [touch locationInView:mapView];
// Check if touch is within the view
if (CGRectContainsPoint(viewFrameInMapView, point))
{
tappedOverlay = overlay;
[mapView selectAnnotation:tappedOverlay animated:NO];
break;
}
}
}
}
};
When I do this, Both didSelectAnnotationView and didDeselectAnnotationView are called for the same MKAnnotationView object. My question is, why Deselect method is being called?
When I manually select Annotation, It does not call Deselect method, meaning it works fine.
Thank You !!!

Got solution myself. As Tap occurs on overlay but outside annotation boundary, Deselect method is called. Annotation that I select programatically in TouchesBegan, will be deselected because the method is called after Touchesbegan method is called.

Related

viewForAnnotation didn't moves the userLocation

I've implemented - (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation { method to change the default location of userLocation Annotation. Inside this method I've used:
if (annotation==(mapView.userLocation)) {
MKAnnotationView *myAnotation = [[MKAnnotationView alloc] init];
myAnotation.image = self.userPointImage.image;
myAnotation.centerOffset = CGPointMake(0, 250);
return myAnotation;
}
else
{
return nil;
}
Which successfully moves the default userLocation annotation to center bottom. But as I'm also using heading so when move my phone it is still rotating the map from center.
- (void)locationManager:(CLLocationManager *) manager didUpdateHeading:(CLHeading *) newHeading {
[self.mapView setUserTrackingMode:MKUserTrackingModeFollowWithHeading animated:true];
}
But the userLocation annotation is below. So how to do that?
UPDATE I found this question and tried applying it. But as I'm using heading so it crashes the app.
In my didUpdateUserLocation method I added [self moveCenterByOffset:CGPointMake(0, 200) from:userLocation.coordinate]; and this method is as follows:
- (CLLocationCoordinate2D)moveCenterByOffset:(CGPoint)offset from:(CLLocationCoordinate2D)coordinate
{
CGPoint point = [self.mapView convertCoordinate:coordinate toPointToView:self.mapView];
point.x += offset.x;
point.y += offset.y;
CLLocationCoordinate2D center = [self.mapView convertPoint:point toCoordinateFromView:self.mapView];
return center;
}
and then in my didUpdateUserLocation method I updated it as: MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance([self moveCenterByOffset:CGPointMake(0, -250) from:userLocation.coordinate], 800.0f, 200.0f); and [mapView setRegion:region animated:NO]; as I'm using heading so animation is set to NO. But now as I run the code it starts jerking between center and the new point.
MKCoordinateRegion region = mapClinicsView.region;
set 'region.center'
It will helps you to navigate your map center position
make sure 'region.center' and annotation center latitute and longitute is same

Google maps iOS sdk get tapped overlay coordinates

i'm working with Google maps iOS sdk.
i want to get the coordinates of the touched point when user taps an overlay.
there is this delegate method:
- (void)mapView:(GMSMapView *)mapView didTapAtCoordinate:(CLLocationCoordinate2D)coordinate
but it's not called when you tap an overlay or a marker.
can i call it programmatically (but coordinate parameter is missing-that's what i want..)?
or get location from this:
- (void) mapView: (GMSMapView *) mapView didTapOverlay: (GMSOverlay *) overlay
any suggestion's precious!
thanks!
UPDATE 6/2/15
Just staple a UITapGestureRecognizer onto the map and then extract the coordinate from the touch point. Your didTapAtCoordinate and didTapAtOverlay will continue to fire as before.
UITapGestureRecognizer *touchTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapTouchTap:)];
[self.view addGestureRecognizer:touchTap];
-(void)tapTouchTap:(UITapGestureRecognizer*)touchGesture
{
CGPoint point = [touchGesture locationInView:self.view];
CLLocationCoordinate2D coord = [self.mapView.projection coordinateForPoint:point];
NSLog(#"%f %f", coord.latitude, coord.longitude);
}
ORIGINAL POST
You are likely missing two snippets of code.
Adopt the GMSMapViewDelegate in your header file:
#interface Map : UIViewController <GMSMapViewDelegate>
You also need to set the delegate in your viewDidLoad:
self.mapView.delegate = self;
Now this should fire for you:
- (void)mapView:(GMSMapView *)mapView didTapAtCoordinate:(CLLocationCoordinate2D)coordinate
{
NSLog(#"You tapped at %f,%f", coordinate.latitude, coordinate.longitude);
}
If you just want to get the position of the marker, there is a position property
CLLocationCoordinate2D coord = marker.position;
When this delegate method gets called
- (void)mapView:(GMSMapView *)mapView didTapAtCoordinate:(CLLocationCoordinate2D)coordinate
its for screen taps that have no markers or overlays from my expierience.
GMSOverlay is a bit different because its a super class to a GMSMarker. You just need to subclass GMSOverlay for your custom overlays and add a position property. When you create the overlay, say in didTapAtCoordinate, you can assign the position (GPS coord) in there.
You could set the circle not tappable (default behaviour) and catch all the clicks on the map with the didTapAtCoordinate delegate.
Then when this event is triggered you could loop over all your circles to check if the user tapped inside one of the circles or outside.
self.mapview.settings.consumesGesturesInView = NO;
Add this line in viewdidload or after allocating.
And then implement this delegate method.
- (void)mapView:(GMSMapView *)mapView didTapAtCoordinate:(CLLocationCoordinate2D)coordinate
{
NSLog(#"%g",coordinate);
}
- (void)mapView:(GMSMapView *)mapView didTapOverlay:(GMSOverlay *)overlay
{
GMSCircle *circle=(GMSCircle *)overlay;
CLLocationCoordinate2D touchCoOrdinate= circle.position;
NSLog(#"%g",touchCoOrdinate);
}
So you can't get the didTapAtCoordinateMethod to fire for an overlay tap. However I did find a slightly dirty workaround.
Using an overlay to draw polylines, we need a way to recognize where a polyline was tapped. So when drawing polylines we can build them like this.
//draw line
GMSPolyline *polyline = [GMSPolyline polylineWithPath:path];
polyline.strokeColor = [UIColor purpleColor];
polyline.tappable = TRUE;
polyline.map = self.googleMapView;
polyline.title = routestring;
Where routestring is a built string from
routestring = [NSString stringWithFormat:#"%#/%#/%#",lat,lng,[annnotationobject objectForKey:#"linkId"]];
And lat and lng are string values of our coordinates. The last part is an ID for the polyline.
The routestring is storing the coordinates and an ID separated by '/' so that we can use component path of string to look them up later. This is assigned to the polylines title.
Now when the overlay is tapped:
-(void)mapView:(GMSMapView *)mapView didTapOverlay:(GMSOverlay *)overlay{
NSString *path = overlay.title;
//Finding componentpaths of string
NSArray *pathparts = [path pathComponents];
NSString *lat = [pathparts objectAtIndex:0];
NSString *lng = [pathparts objectAtIndex:1];
NSString *linkID = [pathparts objectAtIndex:2];
//Here we are building a marker to place near the users tap location on the polyline.
GMSMarker *marker = [GMSMarker markerWithPosition:CLLocationCoordinate2DMake([lat doubleValue],[lng doubleValue])];
marker.title = overlay.title;
marker.snippet = #"ROUTE DATA";
marker.map = self.googleMapView;
//This will popup a marker window
[self.googleMapView setSelectedMarker:marker];
}
We can use component paths of the string we built(separated by "/")to get the latitude and longitude coordinates from the polyline. Then assign them a marker to popup information on the overlay item.
You can subclass UITapGestureRecognizer and override its touchesEnded to retrieve the touch point, than retrieve the coordinate with GMSMapView's coordinateForPoint.
Subclass UITapGestureRecognizer and add it to your mapView:
self.touchTap = [[TouchGestureRecognizer alloc] initWithTarget:self action:#selector(tapTouchTap)];
self.touchTap.mapView = self.mapView;
[self.view addGestureRecognizer:self.touchTap];
TouchDownGestureRecognizer.h:
#import <UIKit/UIKit.h>
#interface TouchGestureRecognizer : UITapGestureRecognizer
#property GMSMapView *mapView;
#end
TouchDownGestureRecognizer.m:
#import "TouchGestureRecognizer.h"
#import <UIKit/UIGestureRecognizerSubclass.h>
#implementation TouchGestureRecognizer
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesEnded:touches withEvent:event];
UITouch *touch = touches.allObjects[0];
CGPoint point = [touch locationInView:self.mapView];
CLLocationCoordinate2D coord = [self.mapView.projection coordinateForPoint:point];
NSLog(#"%f %f", coord.latitude, coord.longitude);
}
#end

How to add touch gesture to map but ignore touches on pins and annotations?

I have a mapview that uses MKCircles to display radius information for certain user actions.
What I want to do, is allow the user to dismiss the MKCircle when they touch the map. However, I would like the MKCircle to NOT dismiss should the user touch any of the other pins or the MKCircle itself.
Any ideas?
Here is my current code, which dismisses the MKCircle when any part of the map is touched:
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(deactivateAllRadars)];
[tap setCancelsTouchesInView:NO];
[_mapView addGestureRecognizer:tap];
In the deactivateAllRadars method, you can use hitTest:withEvent: to tell whether an MKAnnotationView has been tapped or not.
An example of this is shown in How can I catch tap on MapView and then pass it to default gesture recognizers? (it's the second code sample).
This will let you avoid removing the circle if an annotation has been tapped.
If an annotation has not been tapped, you can then check if an MKCircle was tapped by getting the coordinates of the touch (see How to capture Tap gesture on MKMapView for an example) and seeing if the distance from the touch to the circle's center is greater than its radius.
Note that the deactivateAllRadars should be changed to deactivateAllRadars:(UITapGestureRecognizer *)tgr because it will need information from the associated gesture recognizer. Also be sure to add a colon at the end of the method's selector where you alloc+init tap.
For example:
-(void)deactivateAllRadars:(UITapGestureRecognizer *)tgr
{
CGPoint p = [tgr locationInView:mapView];
UIView *v = [mapView hitTest:p withEvent:nil];
id<MKAnnotation> ann = nil;
if ([v isKindOfClass:[MKAnnotationView class]])
{
//annotation view was tapped, select it...
ann = ((MKAnnotationView *)v).annotation;
[mapView selectAnnotation:ann animated:YES];
}
else
{
//annotation view was not tapped, deselect if some ann is selected...
if (mapView.selectedAnnotations.count != 0)
{
ann = [mapView.selectedAnnotations objectAtIndex:0];
[mapView deselectAnnotation:ann animated:YES];
}
//remove circle overlay if it was not tapped...
if (mapView.overlays.count > 0)
{
CGPoint touchPoint = [tgr locationInView:mapView];
CLLocationCoordinate2D touchMapCoordinate
= [mapView convertPoint:touchPoint toCoordinateFromView:mapView];
CLLocation *touchLocation = [[CLLocation alloc]
initWithLatitude:touchMapCoordinate.latitude
longitude:touchMapCoordinate.longitude];
CLLocation *circleLocation = [[CLLocation alloc]
initWithLatitude:circleCenterLatitude
longitude:circleCenterLongitude];
CLLocationDistance distFromCircleCenter
= [touchLocation distanceFromLocation:circleLocation];
if (distFromCircleCenter > circleRadius)
{
//tap was outside the circle, call removeOverlay...
}
}
}
}
This is my Swift 2.1 compatible version:
func didTapOnMap(recognizer: UITapGestureRecognizer) {
let tapLocation = recognizer.locationInView(self)
if let subview = self.hitTest(tapLocation, withEvent: nil) {
if subview.isKindOfClass(NSClassFromString("MKNewAnnotationContainerView")!) {
print("Tapped out")
}
}
}
MKNewAnnotationContainerView is a private inner class, so you cannot compare directly like:
if subview is MKNewAnnotationContainerView {
}

How to get a CGPoint from a tapped location?

I'm working on a graphing calculator app for the iPad, and I wanted to add a feature where a user can tap an area in the graph view to make a text box pop up displaying the coordinate of the point they touched. How can I get a CGPoint from this?
you have two way ...
1.
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
CGPoint location = [touch locationInView:touch.view];
}
here,you can get location with point from current view...
2.
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapped:)];
[tapRecognizer setNumberOfTapsRequired:1];
[tapRecognizer setDelegate:self];
[self.view addGestureRecognizer:tapRecognizer];
here,this code use when you want to do somthing with your perticular object or subview of your mainview
Try This
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
// Get the specific point that was touched
CGPoint point = [touch locationInView:self.view];
NSLog(#"X location: %f", point.x);
NSLog(#"Y Location: %f",point.y);
}
You can use "touchesEnded" if you'd rather see where the user lifted their finger off the screen instead of where they touched down.
Just want to toss in a Swift 4 answer because the API is quite different looking.
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = event?.allTouches?.first {
let loc:CGPoint = touch.location(in: touch.view)
//insert your touch based code here
}
}
OR
let tapGR = UITapGestureRecognizer(target: self, action: #selector(tapped))
view.addGestureRecognizer(tapGR)
#objc func tapped(gr:UITapGestureRecognizer) {
let loc:CGPoint = gr.location(in: gr.view)
//insert your touch based code here
}
In both cases loc will contain the point that was touched in the view.
it's probably better and simpler to use a UIGestureRecognizer with the map view instead of trying to subclass it and intercepting touches manually.
Step 1 : First, add the gesture recognizer to the map view:
UITapGestureRecognizer *tgr = [[UITapGestureRecognizer alloc]
initWithTarget:self action:#selector(tapGestureHandler:)];
tgr.delegate = self; //also add <UIGestureRecognizerDelegate> to #interface
[mapView addGestureRecognizer:tgr];
Step 2 : Next, implement shouldRecognizeSimultaneouslyWithGestureRecognizer and return YES so your tap gesture recognizer can work at the same time as the map's (otherwise taps on pins won't get handled automatically by the map):
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldRecognizeSimultaneouslyWithGestureRecognizer
:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
Step 3 : Finally, implement the gesture handler:
- (void)tapGestureHandler:(UITapGestureRecognizer *)tgr
{
CGPoint touchPoint = [tgr locationInView:mapView];
CLLocationCoordinate2D touchMapCoordinate
= [mapView convertPoint:touchPoint toCoordinateFromView:mapView];
NSLog(#"tapGestureHandler: touchMapCoordinate = %f,%f",
touchMapCoordinate.latitude, touchMapCoordinate.longitude);
}
If you use an UIGestureRecognizer or UITouch object you can use the locationInView: method to retrieve the CGPoint within the given view the user touched.
func handleFrontTap(gestureRecognizer: UITapGestureRecognizer) {
print("tap working")
if gestureRecognizer.state == UIGestureRecognizerState.Recognized {
`print(gestureRecognizer.locationInView(gestureRecognizer.view))`
}
}

add mkannotation on touch map

I want to add a mkannotation to my mkmapview when an user taps over the map so they can choose a location.
I've read about the drag&drop but that's a bit annoying if you want to move to the other corner of the city because you have to move step by step the pin.
How can I get the coordinate where a user taps and move my pin there?
Thanks!
Use UITapGestureRecognizer to get the CGPoint and coordinate of tapped point.
UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(addPin:)];
[recognizer setNumberOfTapsRequired:1];
[map addGestureRecognizer:recognizer];
[recognizer release];
then add your target action
- (void)addPin:(UITapGestureRecognizer*)recognizer
{
CGPoint tappedPoint = [recognizer locationInView:map];
NSLog(#"Tapped At : %#",NSStringFromCGPoint(tappedPoint));
CLLocationCoordinate2D coord= [map convertPoint:tappedPoint toCoordinateFromView:map];
NSLog(#"lat %f",coord.latitude);
NSLog(#"long %f",coord.longitude);
// add an annotation with coord
}
On ios < 3.2, you can use this snippet:
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if ( touch.tapCount == 1 ) {
UITouch *touch = [[event allTouches] anyObject];
if (CGRectContainsPoint([map frame], [touch locationInView:self.view]))
{
CLLocationCoordinate2D coord=
[map convertPoint:tappedPoint toCoordinateFromView:map];
NSLog(#"lat %f",coord.latitude);
NSLog(#"long %f",coord.longitude);
// add an annotation with coord
// or (as example before)
// [self addPin];
}
}
}
it's similar, but don't use UIGestureRecognizer.
Hope this helps.

Resources