I'm trying to create a custom map callout that looks like so:
As you can see, I need custom pins and custom callouts. The color of the pins are based on a rating as is the color of the circle in the callout.
Here's the code that I have so far:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
if ([annotation isKindOfClass:[CustomAnnotation class]])
{
static NSString *identifier = #"CustomAnnotation";
MKPinAnnotationView *annotationView = (MKPinAnnotationView *)[self.map dequeueReusableAnnotationViewWithIdentifier:identifier];
if (annotationView == nil)
{
annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
annotationView.enabled = YES;
annotationView.canShowCallout = NO;
annotationView.animatesDrop = NO;
}
else
{
annotationView.annotation = annotation;
}
CustomAnnotation *myAnnotation= (CustomAnnotation *)annotation;
// Based on the score, set the pin image
if ([myAnnotation.score intValue] == 1)
{
annotationView.image = [UIImage imageNamed:#"dot_purple_xsmall.png"];
}
else if (...)
{
// and so on...
}
return annotationView;
}
return nil;
}
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
{
if (![view.annotation isKindOfClass:[MKUserLocation class]])
{
CustomCalloutView *callout = CustomCalloutView *)[[[NSBundle mainBundle]loadNibNamed:#"MyCallout" owner:self options:nil] objectAtIndex:0];
CGRect calloutViewFrame = callout.frame;
calloutViewFrame.origin = CGPointMake(-calloutViewFrame.size.width / 2 + 15, -calloutViewFrame.size.height);
callout.frame = calloutViewFrame;
// TODO: Set the outlets of the xib
callout.nameLabel.text = #"Testing";
[view addSubview:callout];
}
}
- (void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)view
{
for (UIView *subView in view.subviews)
{
[subView removeFromSuperview];
}
}
The custom pins work fine but when I tap on a pin, all I get is a black view. The app also uses a Navigation and Tab Controller.
I'm working on a map based application with annotations. I am hoping to connect the accessory button in the annotation callout to a segue to a detail view controller.
I have added a new view controller in storyboard with a push segue from the view controller of the map view with the identify firePit
My ViewController.m follows as
#import "ViewController.h"
#import "Annotations.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
locationManager = [[CLLocationManager alloc] init];
[locationManager startUpdatingLocation];
_mapView.showsUserLocation = YES;
_mapView.delegate = self;
//Setting the Visable Region
CLLocationCoordinate2D center = CLLocationCoordinate2DMake(45.036179, -87.134691);
MKCoordinateRegion adjustedRegion = [_mapView regionThatFits:MKCoordinateRegionMakeWithDistance(center, 2000, 1900)];
[self.mapView setRegion: adjustedRegion animated:YES];
//Annotations - Is this the best place?
CLLocationCoordinate2D firePitCoord = CLLocationCoordinate2DMake( 45.037559, -87.130526);
Annotations *firePit = [[Annotations alloc] initWithTitle:#"Fire Pit" Location:firePitCoord];
[self.mapView addAnnotation:firePit];
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
static NSString *identifier = #"Fire Pit";
if ([annotation isKindOfClass:[Annotations class]]) {
MKPinAnnotationView *annotationView = (MKPinAnnotationView *) [mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if (annotationView == nil) {
annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
annotationView.enabled = YES;
annotationView.canShowCallout = YES;
annotationView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
} else {
annotationView.annotation = annotation;
}
return annotationView;
}
return nil;
}
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
[self performSegueWithIdentifier:#"firePit" sender:view]; //thread breakdown 3.1
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(MKAnnotationView *)sender
{
if ([segue.identifier isEqualToString:#"firePit"])
{
MKAnnotationView *annotationView = sender;
[segue.destinationViewController setAnnotation:annotationView.annotation];
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
The app functions in the simulator until I press the accessory button in the annotation callout. It then crashes and alerts me to a thread breakdown in the [self performSegueWithIdentifier:#"firePit" sender:view] method (as commented in the code).
If anyone can offer insight into this problem I would really appreciate it. I can't seem to find assistance from any other resources but if you you have a recommendation I would love a link.
Thanks
has somebody an idea why i can't force the mapView callout to use a detailDisclosureBtn instead of infoButton?
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
static NSString *identifier = #"HPIPAnnotation";
if ([annotation isKindOfClass:[CoreCityAnnotation class]])
{
MKPinAnnotationView *annotationView = (MKPinAnnotationView *) [_mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if (annotationView == nil) {
annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
} else {
annotationView.annotation = annotation;
}
UIButton *disclosureButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
annotationView.canShowCallout = YES;
annotationView.animatesDrop = YES;
annotationView.rightCalloutAccessoryView = disclosureButton;
return annotationView;
}
return nil;
}
Thats my implementation of the delegate.
The button images for detailDisclosure type and infoLight/infoDark are the same.
https://developer.apple.com/library/ios/documentation/userexperience/conceptual/mobilehig/Controls.html#//apple_ref/doc/uid/TP40006556-CH15-SW4
Why can not add annotations on mapView??? I get array with annotation, then try to put them to map((( map is empty( this my code and viewAnnotation method where i find mistake with my own annotation class(
#implimentation MyClass {
NSMutableArray *_annotation;
}
_agents = [[Database sharedDatabase] agentsList];
_mapView.delegate = self;
[_agents enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[_annotations addObject:[[[BronAgentMapViewAnnotation alloc] initWithAgency:obj] autorelease]];
}];
NSLog(#"num of annot = %d", _annotations.count); //num of annot = 120
_mapView.showsUserLocation = YES;
if (_showUserLocation) {
_mapView.showsUserLocation = YES;
}
[_annotations enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[_mapView addAnnotation: obj];
}];
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
_annotations = [[NSMutableArray array] retain];
}
return self;
}
- (MKAnnotationView *)mapView:(MKMapView *)theMapView viewForAnnotation:(id <MKAnnotation>)annotation
{
// in case it's the user location, we already have an annotation, so just return nil
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil;
// handle our three custom annotations
//
if ([annotation isKindOfClass:[BronAgentMapViewAnnotation class]])
{
// try to dequeue an existing pin view first
static NSString *bronAgentMapViewAnnotationIdentifier = #"BronAgentMapViewAnnotationIdentifier";
MKPinAnnotationView *pinView = (MKPinAnnotationView *)
[_mapView dequeueReusableAnnotationViewWithIdentifier:bronAgentMapViewAnnotationIdentifier];
if (pinView == nil)
{
// if an existing pin view was not available, create one
MKPinAnnotationView *customPinView = [[[MKPinAnnotationView alloc]
initWithAnnotation:annotation reuseIdentifier:bronAgentMapViewAnnotationIdentifier] autorelease];
customPinView.pinColor = MKPinAnnotationColorPurple;
customPinView.animatesDrop = NO;
customPinView.canShowCallout = YES;
// add a detail disclosure button to the callout which will open a new view controller page
//
// note: you can assign a specific call out accessory view, or as MKMapViewDelegate you can implement:
// - (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control;
//
UIButton* rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
[rightButton addTarget:self
action:#selector(showDetails:)
forControlEvents:UIControlEventTouchUpInside];
rightButton.tag = [(BronAgentMapViewAnnotation *) annotation annotationId];
customPinView.hidden = [(BronAgentMapViewAnnotation *) annotation isHidden];
customPinView.rightCalloutAccessoryView = rightButton;
return customPinView;
}
else
{
pinView.rightCalloutAccessoryView.tag = [(BronAgentMapViewAnnotation *) annotation annotationId];
pinView.hidden = [(BronAgentMapViewAnnotation *) annotation isHidden];
pinView.annotation = annotation;
}
return pinView;
}
return nil;
}
Can help?
You need to implement the map delegate. The annotationViews are assigned there.
http://developer.apple.com/library/ios/documentation/MapKit/Reference/MKMapViewDelegate_Protocol/MKMapViewDelegate/MKMapViewDelegate.html
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id < MKAnnotation >)annotation
Try to create your own annotationView's class and call viewforannotation like this:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation{
if ([annotation isKindOfClass:[yourAnnotationClass class]]) {
yourAnnotationView *annotationView = (yourAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:#"annotation"];
if (!annotationView)
annotationView = [[yourAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"annotation" delegate:self];
else
annotationView.annotation = annotation;
return annotationView;
}
return [_mapView viewForAnnotation:annotation];
}
Have you connected your _mapView IBOutlet to the MKMapView you have in your interface? Without it it doesn't know that the MKMapView it is drawing on screen is the one you are calling _mapView and adding annotations to, so it won't ask to draw those annotations.
Use this:
mapView=[[MKMapView alloc]initWithFrame:CGRectMake(0,0,320,460)];
mapView.userInteractionEnabled=YES;
mapView.zoomEnabled=YES;
mapView.delegate=self;
[self.view addSubview:mapView];
[self annotationPlacing];
// 36.777313,-119.706405 36.120838,-115.33682 47.798397,-121.519775 47.606337,-122.330618
}
-(void)annotationPlacing
{
NSArray *arrLat=[[NSArray alloc]initWithObjects:#"36.777313",#"36.120838",#"47.798397",#"47.606337", nil];
NSArray *arrLong=[[NSArray alloc]initWithObjects:#"-119.706405",#"-115.33682",#"-121.519775",#"-122.330618", nil];
for(int i=0;i<4;i++)
{
CLLocationCoordinate2D location;
location.latitude=[[arrLat objectAtIndex:i]floatValue];
location.longitude=[[arrLong objectAtIndex:i]floatValue];
MKPointAnnotation *annottion=[[MKPointAnnotation alloc]init];
annottion.coordinate=location;
annottion.title=#"India";
annottion.subtitle=#"Hyderabad";
[arrAnnotation addObject:annottion];
[mapView addAnnotation:annottion];
}
}
I want to check if the annotation is clicked:
So it goes from this:
To :
Here's my code that shows the annotation:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
static NSString *identifier = #"MyLocation";
if ([annotation isKindOfClass:[MyLocation class]]) {
MKAnnotationView *annotationView = (MKAnnotationView *) [_mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if (annotationView == nil) {
annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
annotationView.enabled = YES;
annotationView.canShowCallout = YES;
annotationView.image = [UIImage imageNamed:#"arrest.png"];//here we use a nice image instead of the default pins
annotationView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
} else {
annotationView.annotation = annotation;
}
return annotationView;
}
return nil;
}
the rightCalloutAccesoryView is a blue button inside the callout. But i need to hide something when the user clicks on the annotation.
Does anyone know how to check this/do this?
Thanks is advance
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view {
//do your code
}