Am adding custom overlays to MKMapView and need to clear the map content before adding overlay (i.e when zoomed or panned default map should be invisible)
Something similar to "canReplaceMapContent" in IOS7 and later.
Is there any method to perform this action in IOS6?
Thanks in advance.,
You method below:
- (void) removeMapOverlay {
[self.mapView removeOverlays:[self.mapView overlays]];
NSMutableArray *tempArray = [NSMutableArray arrayWithArray:[self.mapView annotations]];
if ([tempArray containsObject:[MKUserLocation class]]) {
[tempArray removeObject:[MKUserLocation class]];
}
NSArray *annotationArray = [NSArray arrayWithArray:tempArray];
tempArray = nil;
[self.mapView removeAnnotations:annotationArray];
}
Edit:
When you pinch/zoom or pan in the map. There are two delegate methods are available you can use to check map is loading or not?
- (void)mapViewWillStartLoadingMap:(MKMapView *)mapView {
NSLog(#"loading...");
}
- (void)mapViewDidFinishLoadingMap:(MKMapView *)mapView {
NSLog(#"Map loaded...");
}
What i suggest you to use above two methods. Create a bool variable or some means not to load annotations when zoom. How ever i'll keep update if there is other simple way to do it.
You could draw a custom, opaque overlay on top of Apple's maps, but there is still a very slight chance that you will see the map beneath from time to time. I'd recommend an alternative, open source toolset that you can control like Mapbox for complete control.
Related
I have a splitviewcontroller application where the master view controller is a UITableView and the detail view controller contains a MKMapView and a single annotation. When I make a selection in the UITableView the secondary view segues to a different map view with a single annotation.
When I select an annotation and then immediately afterwards (before the annotation popover appears) select a cell I receive an EXC_BAD_ACCESS crash. I used the Zombies tool to try and gather further information and I received this message.
An Objective-C message was sent to a deallocated 'MKPopoverBasedAnnotationCalloutController' object (zombie)
I believe the issue here is that the map view is still trying to display the annotations popover but the annotation has been deallocated.
So far I have tried:
•Setting MKMapView delegate nil on dealloc
Note: I am not using any custom popovers for the annotation. I also had a similar problem when calling [mapView selectAnnotation:mp animated:YES]; and then selecting another cell. I fixed this by just not calling it. This is obviously not an ideal solution.
Any suggestions on how to solve this? Or any insight on whether it is an issue with MapKit or an Issue with my application specifically?
Thanks in Advance,
Chris
I've been able to reproduce the issue consistently and it indeed looks like a bug with Apple's mapkit code. The only way i've been able to fix it was to create a singleton for MKMapView
-(MKMapView*)mapview{
static MKMapView *_mapview = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_mapview = [[MKMapView alloc] initWithFrame:[UIScreen mainScreen].bounds];
_mapview.showsPointsOfInterest = NO;
_mapview.pitchEnabled = NO;
});
return _mapview;
}
An important note, when you remove the map don't clear the annotations yet, wait until you initialize it..
so not here...
-(void)viewDidDisappear:(BOOL)animated{
[super viewDidDisappear:animated];
[self.mapview removeFromSuperview];
self.mapview.delegate = nil; //AND OR IN DEALLOC
}
but here ...
- (void)viewDidLoad{
[super viewDidLoad];
[self.view addSubview:self.mapview];
self.mapview.delegate = self;
[self.mapview removeAnnotations:[self.mapview annotations]];
//RECREATE THOSE ANNOTATIONS
}
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;
}
}
for (MyAnnotation *a in dealerMapView.annotations) {
// Your current position (if shown) is an annotation as well. Thus check for the right class!
if ([a isKindOfClass:[MyAnnotation class]]) {
// insert some smart if statement here, if you have multiple annotations!
[[dealerMapView viewForAnnotation:a] setSelected:YES animated:YES];
}
}
I used the code above code, but info is not displaying on pin.
[self.dealerMapView selectAnnotation:myAnnotation2 animated:YES];
I used this statement and now its displaying the info on annotation.
My app places a pushpin on the map and then selects its using animation so the user has a visual clue and can immediately read the title/subtitle. The following code works in both iOS4 and iOS5, but in iOS5, the annotation doesn't get selected automatically unless I change the animation to NO in the selectAnnotation method.
Any ideas why?
MapAnnotations *pushpin = [[MapAnnotations alloc] initWithCoordinate:coordinate];
pushpin.title = [selectedStation valueForKey:#"name"];
pushpin.subtitle = [selectedStation valueForKey:#"address"];
[stationMap addAnnotation:pushpin];
[stationMap selectAnnotation:pushpin animated:YES];
[pushpin release]; pushpin = nil;
Not sure why it would work before but the animation probably requires the annotation view to be created and ready which is unlikely immediately after adding the annotation.
What you can do is move the selection to the didAddAnnotationViews delegate method which should work on all iOS versions:
- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views
{
for (MKAnnotationView *av in views) {
if ([av.annotation isKindOfClass:[MapAnnotations class]]) {
MapAnnotations *pushpin = (MapAnnotations *)av.annotation;
if (_this_pushpin_is_the_one_to_select) {
[mapView selectAnnotation:av.annotation animated:YES];
break; //or 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];
}
}