Updating MKannotation image without flashing - objective-c

I want to update the images of some of my annotations on a mapview every 5 seconds, however I dont' want to remove and re-add them to the map as this causes them to 'flash' or refresh, (ie disapear then reappear). I want it to be seamless.
I've tried the following:
//get the current icon
UserAnnotation *annotation = [self GetUserIconWithDeviceId:deviceId];
//make a new annotation for it
UserAnnotation *newAnnotation = [[UserAnnotation alloc]
initWithCoordinate: userCoordinates
addressDictionary:nil];
newAnnotation.title = name;
newAnnotation.userDeviceId = deviceId;
NSInteger ageIndicator = [[userLocation objectForKey: #"ageIndicator"] integerValue];
newAnnotation.customImage = [UserIconHelpers imageWithAgeBorder:ageIndicator FromImage: userImage];
//if its not there, add it
if (annotation != nil){
//move it
//update location
annotation.coordinate = userCoordinates;
//add new one
[self.mapView addAnnotation: newAnnotation];
//delete old one
[self.mapView removeAnnotation: annotation];
} else {
//just addd the new one
[self.mapView addAnnotation: newAnnotation];
}
as a thought that if I added the new icon on top I could then remove the old icon, but this still caused the flashing.
Has anyone got any ideas?

In the case where the annotation is not nil, instead of adding and removing, try this:
annotation.customImage = ... //the new image
MKAnnotationView *av = [self.mapView viewForAnnotation:annotation];
av.image = annotation.customImage;

Swift version of Anna answer:
annotation.customImage = ... //the new image
let av = self.mapView.viewForAnnotation(dnwl!)
av?.image = annotation.customImage

It seems you are using your own custom views for the annotations, in that case you can simply add a "refresh" method to your custom view and call it after you have updated the underlying annotation (ie: a custom view -a derived class from MKAnnotationView- is always attached to a potentially custom "annotation" class that conforms to the MKAnnotation protocol)
*) CustomAnnotationView.h
#interface CustomAnnotationView : MKAnnotationView
{
...
}
...
//tell the view to re-read the annotation data it is attached to
- (void)refresh;
*) CustomAnnotationView.m
//override super class method
- (void)setAnnotation:(id <MKAnnotation>)annotation
{
[super setAnnotation:annotation];
...
[self refresh];
}
- (void)refresh
{
...
[self setNeedsDisplay]; //if necessary
}
*) Where you handle the MKMapView and its annotations
for(CustomAnnotation *annotation in [m_MapView annotations])
{
CustomAnnotationView *annotationView = [m_MapView viewForAnnotation:annotation];
[annotationView refresh];
}

Related

Asynchronously setting MKAnnotationView wrong images

I'm asynchronously loading images via AWS S3 and setting the MkMapView image to this image. My s3 code is long but it works everywhere else-and I think I narrowed this down to a #synchronized problem. I'm using this because in my didSelectAnnotationView, I'm rearranging my NSMutableArray which requires thread safety.
My steps are as follows. First, whenever the map screen changes, I download the users in that area and parse the JSON repsonse. I set these responses to an NSMutableArray via
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
#synchronized(self.trainerArray){
[self.trainerArray removeAllObjects];
for(NSDictionary* item in tempDict){
FSTrainer* tempTrainer = [[FSTrainer alloc] initWith:item];
//adding to temp array and map
[self.trainerArray addObject:tempTrainer];
TrainerPin* trainerPin = [[TrainerPin alloc] initWith:tempTrainer];
[self.mapView addAnnotation:trainerPin];
}
}
}
Now when I set this via the viewForAnnotation (with custom reuse identifiers) this all works fine, UNLESS I zoom really fast or erratically, and then the same image gets set twice (the first one).
- (MKAnnotationView *) mapView: (MKMapView *) mapView viewForAnnotation: (id) annotation {
TrainerPin* trainerPinForView = (TrainerPin*)annotation;
TrainerMapImage *pin = (TrainerMapImage *) [self.mapView dequeueReusableAnnotationViewWithIdentifier: [NSString stringWithFormat:#"trainerAnnotation%ld", (long)trainerPinForView.trainer.id]];
if (!pin) {
pin = [[TrainerMapImage alloc] initWithAnnotation: trainerPinForView reuseIdentifier: [NSString stringWithFormat:#"myPin%ld", (long)trainerPinForView.trainer.id]];
} else {
pin.annotation = trainerPinForView;
}
//custom method in TrainerMapImage
[pin asyonchronouslySetImage];
return pin;
}
Another thing to note is that wrapping this in a GCD queue ALWAYS returns the first image for every MKMapView

Correct way to transition between collection view and paged detail view

Currently I have a uicollection view which displays a specific album in the users photos, (ALAssets library).
In my mainView.m I gather the pictures:
+ (ALAssetsLibrary *)defaultAssetsLibrary {
static dispatch_once_t pred = 0;
static ALAssetsLibrary *library = nil;
dispatch_once(&pred, ^{
library = [[ALAssetsLibrary alloc] init];
});
return library;
}
- (void)beginLoadingPhotoInfo {
...
[library enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos
usingBlock:assetGroupEnumerator
failureBlock:^(NSError *error) {NSLog(#"Probs");}
];
}
Load them (the thumbnail version) all into the collection view and that all works well.
Then when a user selects a photo I call this prepareToSegue method: (still in mainView.m)
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if([[segue identifier] isEqualToString:#"showDetail"])
{
NSIndexPath *indexPath = [[self.collectionView indexPathsForSelectedItems] lastObject];
DetailViewController *detailviewcontroller = [segue destinationViewController];
detailviewcontroller.photoArrayIndex = indexPath.row;
//photos array
detailviewcontroller.photosArray = _photoListArray;
}
Currently I am sending an array with the info of the photos and attempting to scroll to the position in the array.
I found this resource here for the horizontal paging:
http://adoptioncurve.net/archives/2013/04/creating-a-paged-photo-gallery-with-a-uicollectionview/
Which allows for paging using a collection view. I wrote that a detailViewController class.
Here's the question. How should I connect the two?
Idea 1: Have my mainView send an integer number representing the photo selected and the detailViewController will then load that one and begin lazy loading the photos.
Idea 2: Somehow preload some of the full screen photos and then send the integer with the spot in the array.
Idea 3: Send both the number and my array object over to the detailViewController so that I don't have to enumerate through the assets library again.
Are any of these the correct approach or did I miss the idea completely?
edit:
What I have in my detail controller is an uicollectionview flow layout with paging enabled.
This is the method where I set up the layout:
- (void) setCollectionView {
[self.collectionView registerClass:[DetailViewCell class] forCellWithReuseIdentifier:#"detailViewCell"];
//Flow Layout
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
[flowLayout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
[flowLayout setMinimumInteritemSpacing:0.0f];
[flowLayout setMinimumLineSpacing:0.0f];
[self.collectionView setPagingEnabled:YES];
[self.collectionView setCollectionViewLayout:flowLayout];
CGFloat pageWidth = self.collectionView.frame.size.width;
NSInteger num = _photosArrayIndex + 1;
CGPoint scrollTo = CGPointMake(pageWidth * num, 0);
NSLog(#"scroll to: %#", NSStringFromCGPoint(scrollTo));
[self.collectionView setContentOffset:scrollTo];
}
What It should do is take the value from my main view and move to that image. Unfortunately it does not. I'm not sure why and also I feel like there is a better way of doing this. It just seems sort of Hackish.
How do I connect the two better controller better and what is the correct way of loading the photos/ how do I get to the photo (in the full size detail view) I was on when they were in a grid layout.
Help is appreciated.
OK, there are three parts to this.
First is the UICollectionViewController subclass to display the gallery of photos (UIImage).
Second is the UIPageViewController subclass to manage the swiping from side to side of each individual PhotoViewController.
Third is the UIViewController subclass (PhotoViewController) to display a single photo.
The storyboard will look something like this...
On the left is a UICollectionViewController this has a segue to the UIPageViewController in the middle. On the right is a UIViewController that has an Identifier set in the properties pane (note, there is no segue to this).
Identifier for the PhotoViewController...
In the PhotoPageViewController I have a custom object...
With a Class type PhotoPageModelController set in the properties pane... This is connected as the dataSource of the PhotoPageViewController.
That's pretty much all the storyboard set up required.
So, the first thing to set up is the PhotoPageModelController. This is the dataSource for the PhotoPageViewController as such will dispense subclasses of UIViewController so that the PhotoPageViewController can display them.
The Model Controller
PhotoPageModelController.h
#class PhotoViewController;
#interface PhotoPageModelController : NSObject <UIPageViewControllerDataSource>
// this is the array of the photos. Either an array of UIImages or objects containing
// them or something. My personal project had an array of photoIDs that I could use to
// pull the photos out of Core Data.
// In this example the array will contain instances of UIImage.
#property (nonatomic, strong) NSArray *photos;
- (PhotoViewController *)viewControllerAtIndex:(NSUInteger)index storyboard:(UIStoryboard *)storyboard;
- (NSUInteger)indexOfViewController:(PhotoViewController *)controller;
#end
PhotoPageModelController.m
#import "PhotoPageModelController.h"
#import "PhotoViewController.h"
#implementation PhotoPageModelController
- (UIImage *)photoAtIndex:(NSUInteger)index
{
// check that the index is in bounds and then return the UIImage to display.
// In my project I just returned the ID of the photo and let the photo
// controller load the actual image from core data. (See below)
if ([self.photos count] == 0
|| index >= [self.photos count]) {
return nil;
}
return self.photos[index];
}
#pragma mark - convenience methods
- (PhotoViewController *)viewControllerAtIndex:(NSUInteger)index storyboard:(UIStoryboard *)storyboard
{
UIImage *photo = [self photoAtIndex:index];
if (photo == nil) {
return nil;
}
// This is why we don't have a segue. We are loading it manually
// from the storyboard using the identifier.
EventPhotoViewController *controller = [storyboard instantiateViewControllerWithIdentifier:#"PhotoViewController"];
// The model controller is where the PhotoViewController gets the actual image from.
// Or an object containing the image with a name, date, details, etc...
// The controller doesn't know anything about the other photos. Only the one it's displaying.
controller.photo = photo;
return controller;
}
- (NSUInteger)indexOfViewController:(PhotoViewController *)controller
{
// Return the index of the given data view controller.
// For simplicity, this implementation uses a static array of model objects and the view controller stores the model object; you can therefore use the model object to identify the index.
return [self.photos indexOfObject:controller.photo];
}
#pragma mark - page view data source
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
// We need to find the index of the current controller so we can get the index
// and then the view controller for the one before it.
NSUInteger index = [self indexOfViewController:(PhotoViewController *) viewController];
if ((index == 0) || (index == NSNotFound)) {
// We have reached the beginning of the photos array so return nil.
// This tells the Page View Controller that there isn't another page.
return nil;
}
index--;
return [self viewControllerAtIndex:index storyboard:viewController.storyboard];
}
// This is the same as above but going forward instead of backward.
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
NSUInteger index = [self indexOfViewController:(EventPhotoViewController *) viewController];
if (index == NSNotFound) {
return nil;
}
index++;
if (index == [self.photoIDs count]) {
return nil;
}
return [self viewControllerAtIndex:index storyboard:viewController.storyboard];
}
#end
OK. So that is the Photo Page Model Controller.
The Page View Controller
Next for the PhotoPageViewController.
PhotoPageViewController.h
#import <Foundation/Foundation.h>
#interface PhotoPageViewController : UIPageViewController
#property (nonatomic, strong) NSArray *photos;
#property (nonatomic) NSUInteger initialIndex;
#end
PhotoPageViewController.m
#import "PhotoPageViewController.h"
#import "PhotoPageModelController.h"
#interface PhotoPageViewController ()
// this property is connected in the storyboard
#property (nonatomic, weak) IBOutlet PhotoPageModelController *modelController;
#end
#implementation PhotoPageViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.modelController.photos = self.photos;
// We use the initialIndex property to get the first controller and display it.
UIViewController *initialController = (UIViewController *)[self.modelController viewControllerAtIndex:self.initialIndex storyboard:self.storyboard];
[self setViewControllers:#[initialController]
direction:UIPageViewControllerNavigationDirectionForward
animated:NO
completion:^(BOOL finished) {
}];
// That's it. Because we have the datasource class it makes this class really easy and short.
// It doesn't even need to know anything about the view controllers it is displaying.
// It's just a dispensing machine.
}
#end
The Photo View Controller
Next is the view controller that will display the actual photo.
All it needs is a property of type UIImage called photo and then a UIImageView to place it in. I'll leave this up to you as you can do it many different ways.
I've put a zoomable UIScrollView in mine so that the user can pinch zoom the photo. I've also got some extra info such as the name of the person who took the photo and the date it was taken etc... Set this up however you like.
The collection view segue
The final part (at last) is going from the collection view to the page view controller.
This is done in prepareForSegue.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"PhotoSegue"]) {
PhotoPageViewController *controller = segue.destinationViewController;
NSIndexPath *selectedIndex = [self.collectionView indexPathsForSelectedItems][0];
// The PageViewController doesn't need anything except the index to start on...
// i.e. the index of the photo that the user just selected.
controller.initialIndex = (NSUInteger)selectedIndex.item;
// ...and the array of photos it will be displaying.
controller.photos = self.photos;
// Everything else is done by the PageViewController.
}
}

extending UIImagePicker controller doesn't help to prevent rotation in io6

My application is set in info.plist to support only portrait mode.
However, the UIImagePickerController, rotates when the user rotates the screen to landscape.
Since in io6 the method shouldAutoRotate is not being called, I tried to extend it like this:
#interface NonRotatingUIImagePickerController : UIImagePickerController
#end
#implementation NonRotatingUIImagePickerController
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return UIInterfaceOrientationMaskPortrait;
}
#end
But it doesn't help. Any idea why?
And in the log I see the above methods being called. The UIImagePickerController at first is displayed in portrait and when the user rotates - it rotates as well instead of staying portrait.
I set the image picker in the view like this:
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (!self.imagePickerController) {
self.imagePickerController = [[NonRotatingUIImagePickerController alloc] init];
self.imagePickerController.delegate = self;
}
return self;
}
- (void)viewDidAppear:(BOOL)animated{
self.imagePickerController.showsCameraControls = NO;
CGRect imagePickerControllerFrame = CGRectMake(0, topBar.frame.size.height, self.view.frame.size.width, self.view.frame.size.height - topBar.frame.size.height - bottomBar.frame.size.height);
self.imagePickerController.view.frame = imagePickerControllerFrame;
self.imagePickerController.allowsEditing = YES;
self.imagePickerController.view.clipsToBounds = YES;
self.imagePickerController.sourceType = UIImagePickerControllerSourceTypeCamera
[self.view.window addSubview:self.imagePickerController.view];
}
self.imagePickerController.view.frame = imagePickerControllerFrame;
// ...
[self.view.window addSubview:self.imagePickerController.view];
Well, that's all totally illegitimate. Apple makes this very clear in the docs:
This class is intended to be used as-is and does not support subclassing. The view hierarchy for this class is private and must not be modified
There is only one correct way to use an image picker controller that uses UIImagePickerControllerSourceTypeCamera - as a fullscreen presented view controller:
BOOL ok = [UIImagePickerController isSourceTypeAvailable:
UIImagePickerControllerSourceTypeCamera];
if (!ok) {
NSLog(#"no camera");
return;
}
NSArray* arr = [UIImagePickerController availableMediaTypesForSourceType:
UIImagePickerControllerSourceTypeCamera];
if ([arr indexOfObject:(NSString*)kUTTypeImage] == NSNotFound) {
NSLog(#"no stills");
return;
}
UIImagePickerController* picker = [UIImagePickerController new];
picker.sourceType = UIImagePickerControllerSourceTypeCamera;
picker.mediaTypes = #[(NSString*)kUTTypeImage];
picker.delegate = self;
[self presentViewController:picker animated:YES completion:nil];
If you want to present a live picture-taking interface inside your own interface, use AVFoundation and the camera capture API that it gives you.
Downloadable working example here:
https://github.com/mattneub/Programming-iOS-Book-Examples/blob/master/ch30p816cameraCaptureWithAVFoundation/p683cameraCaptureWithAVFoundation/ViewController.m
Perhaps you'll consider this answer unhelpful; but I'll just paste a snippet from Apple's documentation:
Important: The UIImagePickerController class supports portrait mode only. This class is intended to be used as-is and does not support subclassing. The view hierarchy for this class is private and must not be modified, with one exception. You can assign a custom view to the cameraOverlayView property and use that view to present additional information or manage the interactions between the camera interface and your code.
UIImagePickerController Doc Link
Sorry to be a kill-joy. You should look for a replacement class. Quickie search shows there are a bunch.

Custom MKMapView callout not responding to touch events

I have a custom UIView that I am displaying as a callout when the user clicks on a custom annotation on an MKMapView.
To achieve this I have subclassed MKAnnotationView and overloaded the -setSelected:selected animated: method as suggested in this answer. Basically, I am adding my custom view as a subview of my MKAnnotationView subclass.
The problem is that I can't interact with the callout, which contains a button and a scrollable webview, at all. What's more, if the callout hides an annotation and I press the callout at the approximate location of that hidden annotation, the callout will get dismissed and a new one will be shown.
// TPMapAnnotationView.m
#implementation TPMapAnnotationView
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
if(selected)
{
TPMapAnnotation* anno = ((TPMapAnnotation*)self.annotation);
QuickInfoView* qi = [[QuickTabView alloc] initWithFrame:CGRectMake(0, 0, 440, 300)];
[qi displayDataForAnnotation:anno];
[self addSubview:qi];
// some animiation code that doesn't change things either way
}
else
{
[[self.subviews objectAtIndex:0] removeFromSuperview];
}
}
The code below creates the TPMapAnnotationView.
// this is in the view controller that contains the MKMapView
- (MKAnnotationView *) mapView:(MKMapView *) mapView viewForAnnotation:(id) annotation
{
if ([annotation isKindOfClass:[TPMapAnnotation class]])
{
TPMapAnnotationView *customAnnotationView = (TPMapAnnotationView *)[myMap dequeueReusableAnnotationViewWithIdentifier:#"TPAnn"];
if (customAnnotationView == nil)
{
customAnnotationView = [[TPMapAnnotationView alloc] initWithAnnotation:annotation
reuseIdentifier:#"TPAnn"];
}
[customAnnotationView setImage:annotationImage];
return customAnnotationView;
}
return nil; // blue radar circle for MKUserLocation class.
}
This is a well known problem. Anything you add on AnnotationView will not detect touches. There is good open source project for this problem. http://dev.tuyennguyen.ca/wp-content/uploads/2011/03/CustomMapAnnotationBlogPart1.zip, http://dev.tuyennguyen.ca/wp-content/uploads/2011/03/CustomMapAnnotationBlogPart21.zip
EDIT:
Yes. I also tried hard to add uibuttons to my own custom annotationView but then I stumbled upon this project and found that his custom annotationView is actually a annotation.
Anyway if you want to to change height of annotatioView then you can set
- (MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>) annotation{
calloutMapAnnotationView.contentHeight = height;
calloutMapAnnotationView.titleHeight = 25;
}
here, titleHeight is property added to CalloutMapAnnotationView which determines height of "gloss" in drawRectMethod
- (void)drawRect:(CGRect)rect {
glossRect.size.height = self.titleHeight;
}
if you are having any difficulty please let me know.
And also the link to original blogpost:
http://dev.tuyennguyen.ca/?p=298
I resolved this problem. Click anything in CalloutView,the map will not get touch.My calloutview is custom have tabbleview
1 - In file MapviewController.h you will add delegate : UIGestureRecognizerDelegate
2 - and in file MapViewController.m implement method - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
-In my mapView when you click 1 time on Map it will go in this method 3 time. So I limit touch will action.the first touch will action.
- In myCalloutView have tabbleView, if tabbleView receive touch It will return false touch for Map, it will make your tabbleview can get touch.It same for your button.
Note : in NSlog hit test View : will have name of view item you want it have touch.
example my view : isEqualToString:#"UITableViewCellContentView"]
static int count=0;
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
NSLog(#"hit test view %#",[touch view]);
if(count >0 && count<=2)
{
count++;
count=count%2;
return FALSE;
}
count++;
if ([[[[touch view] class] description] isEqualToString:#"UITableViewCellContentView"]) {
return FALSE;
}
return TRUE;
}

ViewForAnnotation is not being called

I am trying to make a map, where I can see my current location, and see what the street is called.
so far, I am able to put a pin on my map, but for some reason, I am not getting the callout.
and I have put a NSLog in my viewForAnnotation method, but it is not being called, so i wasn't able to test it.
can someone help me?
-(void)lat:(float)lat lon:(float)lon
{
CLLocationCoordinate2D location;
location.latitude = lat;
location.longitude = lon;
NSLog(#"Latitude: %f, Longitude: %f",location.latitude, location.longitude);
//One location is obtained.. just zoom to that location
MKCoordinateRegion region;
region.center=location;
//Set Zoom level using Span
MKCoordinateSpan span;
span.latitudeDelta=.005f;
span.longitudeDelta=.005f;
region.span=span;
[map setRegion:region animated:TRUE];
//MKReverseGeocoder *geocoder=[[MKReverseGeocoder alloc] initWithCoordinate:location];
//geocoder.delegate=self;
//[geocoder start];
if (cPlacemark != nil) {
[map removeAnnotation:cPlacemark];
}
cPlacemark=[[CustomPlacemark alloc] initWithCoordinate:location];
cPlacemark.title = mPlacemark.thoroughfare;
cPlacemark.subtitle = mPlacemark.locality;
[map addAnnotation:cPlacemark];
[cPlacemark release];
[mLocationManager stopUpdatingLocation];
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil;
// try to dequeue an existing pin view first
if ([annotation isKindOfClass:[CustomPlacemark class]]){
MKPinAnnotationView *pinView=(MKPinAnnotationView *)[map dequeueReusableAnnotationViewWithIdentifier:#"customIdentifier"];
if (!pinView)
{
// if an existing pin view was not available, create one
MKPinAnnotationView* cPinAnnoView = [[[MKPinAnnotationView alloc]
initWithAnnotation:annotation reuseIdentifier:#"customIdentifier"] autorelease];
cPinAnnoView.pinColor = MKPinAnnotationColorPurple;
cPinAnnoView.animatesDrop = YES;
cPinAnnoView.canShowCallout = YES;
// Add button
UIButton *leftButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
[leftButton addTarget:self action:#selector(annotationViewClick:) forControlEvents:UIControlEventTouchUpInside];
cPinAnnoView.leftCalloutAccessoryView = leftButton;
} else
{
pinView.annotation = annotation;
}
return pinView;
}
return nil;
}
Right now I have customized my viewForAnnotation to be like this.
But I still can't get a callout from my pin and the pin remains red.
But it should be purple of nothing at all
I had the same problem which was not setting the MapView delegate to the File Owner.
Open your nib
Right click on the MapView
Drag the delegate to the File's Owner
I had the same problem, as you mentioned. The delegate had been set to ViewController, but the viewForAnnotation selector was not being called. After some checks, I realized if you do not call addAnotation in the main thread, mapView would not call viewForAnnotation, so following update resolved my problem:
Before:
[_mMapView addAnnotation:marker];
After:
dispatch_async(dispatch_get_main_queue(), ^{
[_mMapView addAnnotation:marker];
});
In order to get the viewForAnnotation to be called, add mapView.delegate=self; to e.g. the viewDidLoad method:
- (void)viewDidLoad {
[super viewDidLoad];
mapView.delegate=self;
}
Could it be that your annotation has been added outside the current view area of the MKMapView?
For storyboard, Ctl drag the MKMapView to the orange circle on the bottom bar of ViewController, and select delegate.
This will solve the problem.
As vatrif mentioned in the comments, you must set your delegate BEFORE adding annotations to your MKMapView object.
Others have already explained, odds are high you have not connected your mapview delegate to your controller. Its the first thing to check
i have been working in ios 9 Mapview related app and I experienced the same problem.
somehow I solved my problem, in my case im resizing the mapview.
I added delegate after i resize the mapview. it works now perfectly.!
After having set the delegate for the mapview if still the viewforannotation not getting called then this is something which you have missed - set the self.mapView.showsUserLocation to YES, in interface builder you can tick the shows userLocation option in attributes inspector.