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;
}
}
}
Related
I am a beginner with XCode and I am trying to find a way for the User to input information inside a callbox of a pin they had placed on the map.
So if the User drops a pin and they tap on the pin, I want them to be able to type in a title and a subtitle of that location.
So far, I have created a map using MKMapView and I have implemented UILongPressGestureRecognizer and a void method that fires the moment a pin is created.
I am not sure what to do next? Thank you for your help.
Edit1: I don't have the required reputation to post an image, so here is a link of what I am trying to accomplish: http://imgur.com/gixFxpI
If it is possible to type in the title and subtitle without the use of a disclosure button, that would be swell. Later on I will develop the disclosure button.
Edit2: I have three new questions: 1. Why isn't my disclosure button appearing in my callout? 2. What is reuseidentifier for? Here is what I have so far.
I have embedded a navigation controller to my initial view controller that has the MKMapView in it. I created a second View Controller and I connected the MKMapView to the second View Controller and made my identifier for the second View Controller as 'heylisten' (good old Zelda).
My third question is in the prepareForSegue method, what information needs to go after the dot? destinationViewController.???? = sender.annotation;
// This is your callout box
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
MKAnnotationView *annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"overhere"];
annotationView.canShowCallout = YES;
UIButton *rightDisclosureButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
annotationView.rightCalloutAccessoryView = rightDisclosureButton;
return annotationView;
}
// This is a segue when you tap the right disclosure button
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
[self performSegueWithIdentifier:#"heylisten" sender:view];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(MKAnnotationView *)sender
{
if ([segue.identifier isEqualToString:#"heylisten"])
{
ViewController *destinationViewController = segue.destinationViewController;
// grab the annotation from the sender
destinationViewController = sender.annotation;
}
}
If you're targeting iOS 8, use a UIAlertController:
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIAlertController_class/
If you're targeting iOS 7 or earlier, use a UIAlertView:
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIAlertView_Class/index.html#//apple_ref/occ/cl/UIAlertView
Both of these types have mechanisms to add an input text field and specify a callback to be executed with the text the user typed.
I'm looking to create a 'long hold' pin drop on an MKMapView.
Currently, everything works to the way I want it, when you tap and hold on the Map, it registers the gesture and the code drops a pin.
-(void)viewDidLoad
{
[self.mapView addGestureRecognizer:longPressGesture];
}
-(void)handleLongPressGesture:(UIGestureRecognizer*)sender
{
if(sender.state == UIGestureRecognizerStateBegan || sender == nil)
{
CGPoint point = [sender locationInView:self.mapView];
CLLocationCoordinate2D locCoord;
locCoord = [self.mapView convertPoint:point toCoordinateFromView:self.mapView];
//Drop Pin
}
}
That is, however, it only works if you are not too close to the MKUserLocation annotation (the Blue pulsing dot) otherwise the gesture does not get registered, and the didSelectAnnotationView: function gets called.
Is there a way to Ignore the user taping the MKUserLocation annotation? I was looking for something like setUserEnabled but it doesn't exist for MKAnnotations.
The MKAnnotationView class has an enabled property (not the id<MKAnnotation> objects).
To set enabled on the map view's user location annotation view, get a reference to it in the mapView:didAddAnnotationViews: delegate method:
-(void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views
{
MKAnnotationView *av = [mapView viewForAnnotation:mapView.userLocation];
av.enabled = NO; //disable touch on user location
}
(In viewForAnnotation, you have to return nil to tell the map view to create the view itself so enabled can't be set there -- at least for the user location.)
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;
}
}
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