count the pinnumber in visible rect of mkmapkit - iphone

I have created one Google map view using MK Mapkit and i have annotated pins on different locations now i want to calculate number of pins on visible rect on map view also on when i zoom the map view?
Thanx in advance.

The MKMapView annotationsInMapRect: method will give you the set of annotations in a given map rect.
To get the ones currently visible, pass it the map view's visibleMapRect property.
To detect what annotations are visible after a zoom in, zoom out, or pan, call that method in the regionDidChangeAnimated delegate method:
-(void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
{
NSSet *annSet = [mapView annotationsInMapRect:mapView.visibleMapRect];
NSLog(#"regionDidChangeAnimated: annSet count = %d", annSet.count);
}

No. of pin can be counted by the following delegate method
-(MKAnnotationView *)mapView:(MKMapView *)mapView1 viewForAnnotation:(id )annotation
{
Count ++
NSLog(#"Count : %d", count);
}

Assuming you have a ViewController with mapView as a subview.
- (void)someMethod
{
NSArray *visibleAnnotations = [[self.mapView annotationsInMapRect:self.mapView.visibleMapRect] allObjects];
NSUInteger VisibleAnnotationCounts = visibleAnnotations.count;
...
}
You can also add to a proper MKMapView Delegate Method
{
...
NSArray *visibleAnnotations = [[mapView annotationsInMapRect:mapView.visibleMapRect] allObjects];
NSUInteger VisibleAnnotationCounts = visibleAnnotations.count;
...
}

Related

MKPolygon ontouch detailview

I've created a MKMapView with MKPolygons based on coordinates. There are multiple polygons on the map (look here for an example of what I am re-creating as an app).
What I am trying to do is when the user touches the polygon, it opens a popover view with information about the location. This information is currently stored inside a plist file with the coordinates.
What I currently have so far is that I am able to get touch event and print to the log that the polygon was touched.
The question that I have is:
Can MKPolygonView be used like an MKAnnotationView where once the user taps the pin more information pops up about that current location?
I want to do the same for the polygon view. When touched, the user would see more information about the location that is stored in the plist. If it is possible what would be the best way to get it to work?
My current code is below.
#import "outagemapViewController.h"
#import "MyAnnotation.h"
#import "WildcardGestureRecognizer.h"
#define METERS_PER_MILE 46309.344
#interface outagemapViewController ()
#end
#implementation outagemapViewController
- (void)viewDidLoad {
outages = [[NSArray alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"outages"ofType:#"plist"]];
for (NSDictionary *coloredAreas in outages) {
coordinateData = coloredAreas[#"coords"];
test = coloredAreas[#"outages"];
NSLog(#"test %#", test);
coordsLen = [coordinateData count];
NSLog(#"coords %d", coordsLen);
CLLocationCoordinate2D coords[coordsLen];
for (i=0; i < coordsLen; i++) {
NSString *lat = coordinateData[i];
NSArray *latt = [lat componentsSeparatedByString:#","];
double latitude = [[latt objectAtIndex:0] doubleValue];
double longitude = [[latt objectAtIndex:1] doubleValue];
coords[i] = CLLocationCoordinate2DMake(latitude, longitude);
}
MKPolygon* poly2 = [MKPolygon polygonWithCoordinates:coords count:coordsLen];
poly2.title=#"test";
[self.mapView addOverlay:poly2];
}
}
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay {
{
if ([overlay isKindOfClass:[MKPolygon class]])
{
MKPolygonView* aView = [[MKPolygonView alloc] initWithPolygon:(MKPolygon*)overlay];
int numbers = [test intValue];
if(numbers >= 10){
aView.fillColor = [[UIColor greenColor] colorWithAlphaComponent:0.6];
aView.strokeColor = [[UIColor greenColor] colorWithAlphaComponent:1.0];
aView.lineWidth = 3;
}else if(numbers < 10){
aView.fillColor = [[UIColor yellowColor] colorWithAlphaComponent:0.6];
aView.strokeColor = [[UIColor yellowColor] colorWithAlphaComponent:1.0];
aView.lineWidth = 3;
}
return aView;
}
return nil;
}
}
-(void)viewWillAppear:(BOOL)animated{
CLLocationCoordinate2D zoomLocation;
zoomLocation.latitude = 35.20418;
zoomLocation.longitude = -89.86862;
MKCoordinateRegion viewRegion = MKCoordinateRegionMakeWithDistance(zoomLocation, 0.5*METERS_PER_MILE, 0.5*METERS_PER_MILE);
[_mapView setRegion:viewRegion animated:YES];
WildcardGestureRecognizer * tapInterceptor = [[WildcardGestureRecognizer alloc] init];
tapInterceptor.touchesBeganCallback = ^(NSSet * touches, UIEvent * event) {
UITouch *touch = [touches anyObject];
CGPoint point = [touch locationInView:self.mapView];
CLLocationCoordinate2D coord = [self.mapView convertPoint:point toCoordinateFromView:self.mapView];
MKMapPoint mapPoint = MKMapPointForCoordinate(coord);
for (id overlay in self.mapView.overlays)
{
if ([overlay isKindOfClass:[MKPolygon class]])
{
MKPolygon *poly = (MKPolygon*) overlay;
id view = [self.mapView viewForOverlay:poly];
if ([view isKindOfClass:[MKPolygonView class]])
{
MKPolygonView *polyView = (MKPolygonView*) view;
CGPoint polygonViewPoint = [polyView pointForMapPoint:mapPoint];
BOOL mapCoordinateIsInPolygon = CGPathContainsPoint(polyView.path, NULL, polygonViewPoint, NO);
if (mapCoordinateIsInPolygon) {
// debug(#"hit!");
NSLog(#"hit");
} else {
NSLog(#"miss");
}
}
}
}
};
[self.mapView addGestureRecognizer:tapInterceptor];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Unfortunately, for overlays, there's no built-in touch-detection and callout view like there is for annotations.
You'll have to do the touch-detection manually like you're already doing (and it looks like it should work).
(Even more unfortunate here is that adding a gesture recognizer directly to the overlay view doesn't work -- you have to add it to the whole map and then check whether the touch point is in any overlay.)
For an overlay callout view, once you've detected a touch on an overlay, you can create a custom UIView and do addSubview. I suggest adding it to the map instead of the overlay view and you might be able to use the CGPoint point you are already calculating to determine the frame of the custom callout view.
You might also want to keep a ivar/property reference to the overlay callout view so it can be easily removed and re-added if the user taps on another overlay while the callout for another overlay is already displayed.
Another option which is probably easier is to create a custom UIViewController and present or push it. The specifics of showing it depend on whether you're using a navigation controller and/or storyboard.
If your app is also built for iPad, you could also show the "callout" using a UIPopoverController.
See How do I display a UIPopoverView as a annotation to the map view? (iPad) for a code example (it's with an annotation but you should be able to adapt it for the overlay).
Once you've identified which overlay was tapped, you need to display its associated data which is in your original data source (the outages array). Right now, overlays are created and added but have no reference back to the original data object (outage dictionary in outages array).
(Subclassing MKPolygon to add a custom property has issues and workarounds and creating a completely custom MKOverlay class introduces a lot of other additional work.)
For your current data source structure, a simple, quick (and somewhat crude) option is to set the overlay's title property to the index in the outages array of the outage object associated with the overlay. Since the title property is an NSString and the array index is an integer, we'll convert it to a string:
NSUInteger outageIndex = [outages indexOfObject:coloredAreas];
poly2.title = [NSString stringWithFormat:#"%d", outageIndex];
[self.mapView addOverlay:poly2];
In viewForOverlay, it looks like you're using test (which comes from an outage object) to determine the polygon's color. The value of the externally declared/set test variable will not necessarily be in sync with the overlay the delegate method is currently being called for (the map could call viewForOverlay multiple times for the same overlay and not necessarily in the order you add them). You have to retrieve the outage object based on some property of the overlay parameter. Since we are setting the overlay's title property to the outage's index:
//int numbers = [test intValue]; <-- remove this line
int outageIndex = [overlay.title intValue];
NSDictionary *outageDict = [outages objectAtIndex:outageIndex];
id outageNumbersObject = outageDict[#"outages"];
//replace id above with actual type
//can't tell from code in question whether it's NSString or NSNumber
int numbers = [outageNumbersObject intValue];
//use "numbers" to set polygon color...
Finally, when an overlay is tapped, you use the same method as in viewForOverlay to get the outage object:
if (mapCoordinateIsInPolygon) {
int outageIndex = [overlay.title intValue];
NSDictionary *outageDict = [outages objectAtIndex:outageIndex];
NSLog(#"hit, outageDict = %#", outageDict);
//show view with info from outageDict...
}

Center maps in iOS Programming

How do we follow the user in maps. I want to have the blue dot (user location) be in the center of the map, But I also what to allow the user to zoom in and zoom out and then after a couple seconds zoom in back in the user location.
My Educated Guess for the Solution: We detect if the user is zooming in or out, after three seconds of no zooming in or out detection, we starting follow the user :). Your HELP would be awesome :)
This code zoom in the user location but doesn't delay for zoom in and out:
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation {
MKCoordinateRegion userLocation = MKCoordinateRegionMakeWithDistance(newLocation.coordinate, 1500.0, 1500.0); [mapView setRegion:userLocation animated:YES];
}
A quick look in the docs reveals the magic.
Set the userTrackingMode of your map to MKUserTrackingModeFollow.
See here.
Update:
Since you've updated your question, here's the new answer.
To recenter the map to the user location i would recommend to write a simple helper Method:
- (void)recenterUserLocation:(BOOL)animated{
MKCoordinateSpan zoomedSpan = MKCoordinateSpanMake(1000, 1000);
MKCoordinateRegion userRegion = MKCoordinateRegionMake(self.mapView.userLocation.coordinate, zoomedSpan);
[self.mapView setRegion:userRegion animated:animated];
}
And now you should call it after a short delay if user has stopped moving the map. You can do this in the regionDidChange delegate method of the mapView.
But you can get problems if you don't lock the reset-method if the user changes the region multiple times before it really resets the map. So it would be wise to make a flag if it is possible to recenter the map. Like a property BOOL canRecenter.
Init it with YES and update the recenterUserLocation method to:
- (void)recenterUserLocation:(BOOL)animated{
MKCoordinateSpan zoomedSpan = MKCoordinateSpanMake(1000, 1000);
MKCoordinateRegion userRegion = MKCoordinateRegionMake(self.mapView.userLocation.coordinate, zoomedSpan);
[self.mapView setRegion:userRegion animated:animated];
self.canRecenter = YES;
}
Now you can call it safely after the user has moved the map in any way with a small delay:
- (void)mapView:(MKMapView *)mMapView regionDidChangeAnimated:(BOOL)animated{
if (self.canRecenter){
self.canRecenter = NO;
[self performSelector:#selector(recenterUserLocation:) withObject:#(animated) afterDelay:3];
}
}
I had the same problem. I guessed:
If the user drag the map, he wants to stay on that position.
If the user do nothing or reset to show current location, I need to follow the user.
I added a reset button to show the current user location like this:
On the reset button clicked, changed the needToCenterMap to TRUE
Added a drag gesture recognizer on map
// Map drag handler
UIPanGestureRecognizer* panRec = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(didDragMap:)];
- (void)didDragMap:(UIGestureRecognizer*)gestureRecognizer {
if (gestureRecognizer.state == UIGestureRecognizerStateEnded){
NSLog(#"Map drag ended");
self.needToCenterMap = FALSE;
}
}
Followed the user on map depending on needToCenterMap flag
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
if (self.needToCenterMap == TRUE)
[mapView setCenterCoordinate:userLocation.location.coordinate animated:YES];
}
I made a little example to show how you can delegate this job to the Map SDK.
Of course you could listen to the Location change but MKUserTrackingModeFollow automatically does this for you, so just a single line of code
#import <MapKit/MapKit.h>
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
MKMapView *mapView = [[MKMapView alloc] initWithFrame:self.view.frame];
//Always center the dot and zoom in to an apropriate zoom level when position changes
[mapView setUserTrackingMode:MKUserTrackingModeFollow];
//don't let the user drag around the the map -> just zooming enabled
[mapView setScrollEnabled:NO];
[self.view addSubview:mapView];
}
Then the app looks like this:
For more information just read the Apple Documentation:
http://developer.apple.com/library/ios/#documentation/MapKit/Reference/MKMapView_Class/MKMapView/MKMapView.html
This shell do the trick: mkMapview.showsUserLocation = YES;

iphone - MKMapView callout doesn't show on iPhone 4 with iOS 4.1

I have a problem with MKMapView. I add annotations like that:
// set up new points
for(int i = 0; i < [_locations count]; i++) {
PPlace * place = [_locations objectAtIndex:i];
PlaceAnnotation * placeAnnotation = [[PlaceAnnotation alloc] initWithPlace:place];
// if annotation is for currently selected place
placeAnnotation.isCurrent = i == currentIndexPath.row;
[self.mapView addAnnotation:placeAnnotation];
if (placeAnnotation.isCurrent) {
[self.mapView selectAnnotation:placeAnnotation animated:YES];
}
[placeAnnotation release];
}
So I try to display callout bouble immediately after added, not after annotation pin is tapped.
Everything works fine in simulator, also on iPhone 3GS with iOS 4.3.2. However, the callouts do not show on iPhone 4 with iOS 4.1 (they show only after pin is tapped). Any idea how to solve this?
My guess is that you did not assign a value to the title property of your annotation class. Even though you may set canShowCallout to YES, the call out bubble will not show unless you have something in your title.
try adding
placeAnnotation.canShowCallout = YES;
so it looks like:
// set up new points
for(int i = 0; i < [_locations count]; i++) {
PPlace * place = [_locations objectAtIndex:i];
PlaceAnnotation * placeAnnotation = [[PlaceAnnotation alloc] initWithPlace:place];
// if annotation is for currently selected place
placeAnnotation.isCurrent = i == currentIndexPath.row;
[self.mapView addAnnotation:placeAnnotation];
if (placeAnnotation.isCurrent) {
[self.mapView selectAnnotation:placeAnnotation animated:YES];
placeAnnotation.canShowCallout = YES;
}
[placeAnnotation release];
}
hope this helps!
WeSaM
You will need to implement the following method:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
You can find the documentation here
You are calling it at the wrong time. You can't select it until after it has loaded.
Use the delegate method of the MKMapView:
- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views
And from inside of that method call:
[yourMapView selectAnnotation: yourAnnotation animated: YES];

iPhone - MapKit : How may I customize the color and alpha of an overlay on the fly?

I have a MapView onto which I put some overlays (MKPolygon).
I have to maintain groups of MKPolygon.
For this, I have a PolyGonGroupClass that keeps each poly and the color and alpha that should be used to display all these polygons.
So, each time I find a poly, I add it as a map overlay and memorize in into the correct group of polys.
All my groups of poly are kepts into an instance var.
[self.mapView addOverlay:poly];
[thecorrectpolygroup addObject:poly];
...
[self.mypolygroups addObject:thecorrectpolygroup];
#interface PolyGonGroupClass : NSObject {
UIColor* __color;
float __alpha;
NSMutableArray* __polygons;
}
Well...
At this point, how may I tell to the MapView the color and alpha of each poly and update them on the fly when one of these color change ?
I found a - (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay method, but I don't see how I may use it to retrieve the values I put into the memorised values.
I guess I should use the overlay parameter, but how may I retrieve the correct group of poly that correspond to that overlay ?
In viewForOverlay, you can loop through your mypolygroups array and check if the overlay is in that group's polygons array:
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay
{
if ([overlay isKindOfClass:[MKPolygon class]])
{
MKPolygonView *pv = [[[MKPolygonView alloc] initWithPolygon:overlay] autorelease];
for (PolyGonGroupClass *pgc in mypolygroups)
{
if ([pgc.polygons containsObject:overlay])
{
pv.fillColor = pgc.color;
pv.alpha = pgc.alpha;
break;
}
}
return pv;
}
return nil;
}
However, for this to work right, you'll have to slightly change where you call addOverlay. In the code you've shown, you are calling addOverlay before adding it to the polygons array and before adding the group to the mypolygroups array. This can cause viewForOverlay to fire before the arrays contain data.
Instead, move just the addOverlay calls to after the mypolygroups array is fully populated. So after adding the last group to mypolygroups:
...
[self.mypolygroups addObject:theLastPolygroup];
for (PolyGonGroupClass *pgc in mypolygroups)
{
for (MKPolygon *p in pgc.polygons)
{
[mapView addOverlay:p];
}
}
Finally, to update a group's color and alpha later after it's already on the map, you can do something like this:
//pgc is some instance of PolyGonGroupClass
pgc.alpha = newAlpha;
pgc.color = newColor;
for (MKPolygon *p in pgc.polygons)
{
MKPolygonView *pv = (MKPolygonView *)[mapView viewForOverlay:p];
pv.alpha = pgc.alpha;
pv.fillColor = pgc.color;
}

How to trigger MKAnnotationView's callout view without touching the pin?

I'm working on a MKMapView with the usual colored pin as the location points. I would like to be able to have the callout displayed without touching the pin.
How should I do that? Calling setSelected:YES on the annotationview did nothing. I'm thinking of simulate a touch on the pin but I'm not sure how to go about it.
But there is a catch to get benvolioT's solution to work, the code
for (id<MKAnnotation> currentAnnotation in mapView.annotations) {
if ([currentAnnotation isEqual:annotationToSelect]) {
[mapView selectAnnotation:currentAnnotation animated:FALSE];
}
}
should be called from - (void)mapViewDidFinishLoadingMap:(MKMapView *)mapView, and nowhere else.
The sequence in which the various methods like viewWillAppear, viewDidAppear of UIViewController and the - (void)mapViewDidFinishLoadingMap:(MKMapView *)mapView is called is different between the first time the map is loaded with one particular location and the subsequent times the map is displayed with the same location. This is a bit tricky.
Ok, here's the solution to this problem.
To display the callout use MKMapView's selectAnnotation:animated method.
Assuming that you want the last annotation view to be selected, you can put the code below:
[mapView selectAnnotation:[[mapView annotations] lastObject] animated:YES];
in the delegate below:
- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views
{
//Here
[mapView selectAnnotation:[[mapView annotations] lastObject] animated:YES];
}
Ok, to successfully add the Callout you need to call selectAnnotation:animated after all the annotation views have been added, using the delegate's didAddAnnotationViews:
- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views{
for (id<MKAnnotation> currentAnnotation in mapView.annotations) {
if ([currentAnnotation isEqual: annotationToSelect]) {
[mapView selectAnnotation:currentAnnotation animated:YES];
}
}
}
After trying a variety of answers to this thread, I finally came up with this. It works very reliably, I have yet to see it fail:
- (void)mapView:(MKMapView *)aMapView didAddAnnotationViews:(NSArray *)views;
{
for(id<MKAnnotation> currentAnnotation in aMapView.annotations)
{
if([currentAnnotation isEqual:annotationToSelect])
{
NSLog(#"Yay!");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.3 * NSEC_PER_SEC), dispatch_get_current_queue(), ^
{
[aMapView selectAnnotation:currentAnnotation animated:YES];
});
}
}
}
The block is used to delay slightly, as without it the callout may not be shown correctly.
This does not work for me. I suspect a bug in the MapKit API.
See this link for details of someone else for who this is not working:
http://www.iphonedevsdk.com/forum/iphone-sdk-development/19740-trigger-mkannotationview-callout-bubble.html#post110447
--edit--
Okay after screwing with this for a while, here is what I've been able to make work:
for (id<MKAnnotation> currentAnnotation in mapView.annotations) {
if ([currentAnnotation isEqual:annotationToSelect]) {
[mapView selectAnnotation:currentAnnotation animated:FALSE];
}
}
Note, this requires implementing - (BOOL)isEqual:(id)anObject for your class that implements the MKAnnotation protocol.
If you just want to open the callout for the last annotation you added, try this, works for me.
[mapView selectAnnotation:[[mapView annotations] lastObject] animated:YES];
The problem with calling selectAnnotation from - (void)mapViewDidFinishLoadingMap:(MKMapView *)mapView is that, as the name implies, this event is only triggered once your MapView loads initially, so you won't be able to trigger the annotation's callout if you add it after the MapView has finished loading.
The problem with calling it from - (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views is that your annotation may not be on-screen when selectAnnotation is called which would cause it to have no effect. Even if you center your MapView's region to the annotation's coordinate before adding the annotation, the slight delay it takes to set the MapView's region is enough for selectAnnotation to be called before the annotation is visible on-screen, especially if you animate setRegion.
Some people have solved this issue by calling selectAnnotation after a delay as such:
-(void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views {
[self performSelector:#selector(selectLastAnnotation)
withObject:nil afterDelay:1];
}
-(void)selectLastAnnotation {
[myMapView selectAnnotation:
[[myMapView annotations] lastObject] animated:YES];
}
But even then you may get weird results since it may take more than one second for the annotation to appear on-screen depending on various factors like the distance between your previous MapView's region and the new one or your Internet connection speed.
I decided to make the call from - (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated instead since it ensures the annotation is actually on-screen (assuming you set your MapView's region to your annotation's coordinate) because this event is triggered after setRegion (and its animation) has finished. However, regionDidChangeAnimated is triggered whenever your MapView's region changes, including when the user just pans around the map so you have to make sure you have a condition to properly identify when is the right time to trigger the annotation's callout.
Here's how I did it:
MKPointAnnotation *myAnnotationWithCallout;
- (void)someMethod {
MKPointAnnotation *myAnnotation = [[MKPointAnnotation alloc] init];
[myAnnotation setCoordinate: someCoordinate];
[myAnnotation setTitle: someTitle];
MKCoordinateRegion someRegion =
MKCoordinateRegionMakeWithDistance (someCoordinate, zoomLevel, zoomLevel);
myAnnotationWithCallout = myAnnotation;
[myMapView setRegion: someRegion animated: YES];
[myMapView addAnnotation: myAnnotation];
}
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
{
if (myAnnotationWithCallout)
{
[mapView selectAnnotation: myAnnotationWithCallout animated:YES];
myAnnotationWithCallout = nil;
}
}
That way your annotation is guaranteed to be on-screen at the moment selectAnnotation is called, and the if (myAnnotationWithCallout) part ensures no region setting other than the one in - (void)someMethod will trigger the callout.
I read the API carefully and finally I found the problem:
If the specified annotation is not onscreen, and therefore does not have an associated annotation view, this method has no effect.
So you can wait some time (for example, 3 seconds) and then perform this action. Then it works.
Due to something like the code shown by benvolioT, that I suspect exists in the system, when I used selectAnnotation:animation: method, it did not show the callOut, I guessed that the reason was because it was already selected and it was avoiding from asking the MapView to redraw the callOut on the map using the annotation title and subtitle.
So, the solution was simply to deselect it first and to re-select it.
E.g: First, I needed to do this in Apple's touchMoved method (i.e. how to drag an AnnotationView) to hide the callOut. (Simply using annotation.canShowAnnotation = NO alone does not work, since I suspect that it needs redrawing. The deselectAnnotaiton causes the necessary action. Also, deselecting alone did not do that trick, the callOut disappeared only once and got redrawn straight away. This was the hint that it got reselected automatically).
annotationView.canShowAnnotation = NO;
[mapView deselectAnnotation:annotation animated:YES];
Then, simply using the code below in touchEnded method did not bring back the callOut (The annotation has been automatically selected by the system by that time, and presumably the redrawing of the callOut never occrrs):
annotationView.canShowAnnotation = YES;
[mapView selectAnnotation:annotation animated:YES];
The solution was:
annotationView.canShowAnnotation = YES;
[mapView deselectAnnotation:annotation animated:YES];
[mapView selectAnnotation:annotation animated:YES];
This simply bought back the callOut, presumably it re-initiated the process of redrawing the callOut by the mapView.
Strictly speaking, I should detect whether the annotation is the current annotation or not (selected, which I know it is) and whether the callOut is actually showing or not (which I don't know) and decide to redraw it accordingly, that would be better. I, however, have not found the callOut detection method yet and trying to do so myself is just a little bit unnecessary at this stage.
Steve Shi's response made it clear to me that selectAnnotation has to be called from mapViewDidFinishLoadingMap method. Unfortunately i cannot vote up but i want to say thanks here.
Just add [mapView selectAnnotation:point animated:YES];
Resetting the annotations also will bring the callout to front.
[mapView removeAnnotation: currentMarker];
[mapView addAnnotation:currentMarker];