I'm busy with an iPhone application and for that I'm using a custom MKAnnotation. But I have to press really hard to let it call the method:
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view {
Sometimes it doesn't call it at all.
Does anyone know how I can increase the click range of my MKAnnotation?
Oops, solved it! The thing I did wrong was not calling [mapView deselect annotation..... in the didSelectAnnotationView method.
Related
I have a UITapGestureRecognizer that will hide and show a toolbar over my MKMap when the user taps the Map - simple.
However, when the user taps on an MKMapAnnotation, I do not want the map to respond to a tap in the normal way (above). Additionally, when the user taps elsewhere on the map to de-select an MKAnnotation callout, I also don't want the toolbar to respond. So, the toolbar should only respond when there are no MKAnnotations currently in selected state. Nor should it respond when the user clicks on an annotation directly.
So far, I have being trying the following action that reacts to the tap gesture on the map - however the Annotation View is never detected (the first if statement) and also, the annotation view is also launched regardless of this method.
-(void)mapViewTapped:(UITapGestureRecognizer *)tgr
{
CGPoint p = [tgr locationInView:self.mapView];
UIView *v = [self.mapView hitTest:p withEvent:nil];
id<MKAnnotation> ann = nil;
if ([v isKindOfClass:[MKAnnotationView class]])<---- THIS CONDITION IS NEVER MET BUT ANNOTATIONS ARE SELECTED ANYWAY
{
//annotation view was tapped, select it…
ann = ((AircraftAnnotationView *)v).annotation;
[self.mapView selectAnnotation:ann animated:YES];
}
else
{
//annotation view was not tapped, deselect if some ann is selected...
if (self.mapView.selectedAnnotations.count != 0)
{
ann = [self.mapView.selectedAnnotations objectAtIndex:0];
[self.mapView deselectAnnotation:ann animated:YES];
}
// If no annotation view is selected currently then assume control of
// the navigation bar.
else{
[self showToolBar:self.navigationController.toolbar.hidden];
}
}
}
I need to control the launch of the annotation call out programmatically and detect when the tap event has hit an annotation in order to achieve this.
Any help would be appreciated.
I think you will find the following links very useful:
http://blog.asynchrony.com/2010/09/building-custom-map-annotation-callouts-part-2/
How do I make a MKAnnotationView touch sensitive?
The first link discusses (among other things) how to prevent the propagation of touches to the annotations so that they selectively respond, and the second one how to detect the touches.
I think that because MKMapAnnotationView are on top of MKMapView, they will get the touch event and respond to it (be selected) so I don't think you need to select your annotation manually.
Then, if you have a look at Advanced Gesture Recognizer WWDC 2010 video, you will see that your MKMapView will receive tap event anyway, even if it's below the annotation view. That's probably why your -(void)mapViewTapped:(UITapGestureRecognizer *)tgr method get called.
Apart from that, I can't see why your if ([v isKindOfClass:[MKAnnotationView class]]) is never true. I do the exact same thing in my code and it works fine!
Finally, to answer your last question, if you don't want to do anything when the user is just trying to close the callout, you could keep track of a custom isCalloutOpen boolean value like this:
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view {
//some code
_isCalloutOpen = YES;
}
- (void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)view {
// delay the reset because didDeselectAnnotationView could (and is often) called before your gesture recgnizer handler method get called.
[self performSelector:#selector(resetCalloutOpenState) withObject:Nil afterDelay:0.1];
}
- (void)resetCalloutOpenState {
_isCalloutOpen = NO;
}
- (void)mapViewTapped:(UITapGestureRecognizer *)tgr {
if (_isCalloutOpen) {
return;
}
}
I have an app with a mapview containing various building objects (my own class). Each building has an annotation property which all works great and the user can search a table for the desired building, tap a button and it'll show the relevant annotation.
Now I have a callout button on the annotation which calls a method (showDetails) that pushes a segue to the DetailViewController, but the issue I'm having is getting the right building across to this DetailViewController. All the buildings are stored in an array in the data controller and I'd like to load one from that if possible.
So far I've used the controlWasTapped method where I've simply put:
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
for (Building* theBuilding in self.datacontroller.masterArray){
if(theBuilding.name isEqualToString:view.annotation.title){
NSLog(#"theBuilding's name %#", theBuilding.name);
}
}
This works fine and returns the correct building but I'm stumped as to how to get this to the DetailViewController. I have a PrepareForSegue method but how would I get the correct building to it? Also, now that I have DidSelectAnnotation, is there any need for my showDetails method?
I was thinking I could modify the showDetails method so that it took an argument of Building type and then I could provide the Building details in prepareForSegue but if there is a much better way I'd love to know.
So, what's the best plan? Many thanks
Declare an instance variable in the class that contains the code you posted.
Building *buildingOfInterest;
then
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
for (Building* theBuilding in self.datacontroller.masterArray)
if(theBuilding.name isEqualToString:view.annotation.title) {
buildingOfInterest = theBuilding;
[self performSegueWithIdentifier:#"ShowBuildingSegue" sender:self];
break;
}
}
then:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"ShowBuildingSegue"])
((MyBuildingVC *)segue.destinationViewController).building = buildingOfInterest;
}
and in your target view controller's header file:
#interface MyBuildingVC : UIViewController
#property (strong) Building * building;
#end
Titles and subtitles can be added to the user location that iOS shows using MKUserLocation. When the user taps on the location, these will show in a bubble above the location. The thought bubbles for other annotations can be shown by selecting the annotation with setSelected:animated: from MKAnnotationView. Unfortunately, MKUserLocation does not descend from MKAnnotationView.
How can I programmatically select the user location so the annotation appears over the user location without the user first tapping on it?
The documentation for MKAnnotationView says this about its setSelected:animated: method (and something similar for its selected property):
You should not call this method directly.
Instead, use the MKMapView method selectAnnotation:animated:. If you call it in the didAddAnnotationViews delegate method, you can be sure the annotation view is ready to show the callout otherwise calling selectAnnotation will do nothing.
For example:
-(void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views
{
for (MKAnnotationView *av in views)
{
if ([av.annotation isKindOfClass:[MKUserLocation class]])
{
[mapView selectAnnotation:av.annotation animated:NO];
//Setting animated to YES for the user location
//gives strange results so setting it to NO.
return;
}
}
}
Ok, so I have a map view that has a bunch of annotations on it. Certain annotations when selected need to display extended info in a small table view which i am doing by resizing the mapview to half screen and animating into view a table in the bottom half. If another annotation is selected that doesn't need the extra info then in the didDeselectAnnotationView: method i hide the table and go back to the full map view, rinse and repeat.. So far so good, everything is working great.
The issue i am having though is that if a user selects another annotation while they currently have an annotation selected then didSelectAnnotationView delegate method gets called BEFORE the didDeselectAnnotationView.
This is obviously a problem because i am using these two methods to decide whether or not i need to display/hide the info table below the mapview, see code below:
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
{
if ([view.annotation isKindOfClass:[MapLocation class]])
{
if ([self.selectedAnnotation numberOfEvents] == 1)
{
mapTableViewIsVisible = NO;
}
else if ([self.selectedAnnotation numberOfEvents] > 1)
{
// launch mini tableview
mapTableViewIsVisible = YES;
}
[self loadMapTableViewWithEvents:self.selectedAnnotation.events
forAnnotation:self.selectedAnnotation];
}
}
- (void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)view
{
if ([view.annotation isKindOfClass:[MapLocation class]])
{
mapTableViewIsVisible = NO;
[self loadMapTableViewWithEvents:nil forAnnotation:(MapLocation*)view.annotation];
}
}
So for example if i select an annotation that needs the maptable and i currently have a regular annotation selected then the mapTable is loaded when the didSelectAnnotationView method above is called, however it is immediately hidden again because the didDeselectAnnotationView is called right after.
So far i havent been able to figure out a way to fix this.
Any ideas??
You could check for the case where no annotations are visible in didDeselectAnnotationView and then clean up your tableview on this case only. As all other cases will be handled by didSelectAnnotation view.
Something like:
- (void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)view{
if([[mapView selectedAnnotations] count]==0){
mapTableViewIsVisible = NO;
[self loadMapTableViewWithEvents:nil forAnnotation:(MapLocation*)view.annotation];
}
}
Im my iphone mapview application when i click on the annotation images it get change into pin. Can anyone please tell me how can i correct it?
My idea is to display a image instead of custom pin, even though it is clicked.
When you click on a annotation this delegate will be called
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
{
//I think here you will be adding a annotation(my assumption)
[mapView addAnnotation:yourAnnotation];
//This will call viewForAnnotation again
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
//If you didnt specify the a image for the annotation the default pin image will be assigned here..
}
If my assumption is wrong then post your code here related annotations, So you can get better answers