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.
Related
PREAMBLE
I have implemented a map annotation using MKMapView and MapAnnotation. When tapped a title view appears as depicted in the following image.
I have used the following LOC to implement said map annotation:
// VENUE 1 PIN.
CLLocationCoordinate2D venue1Location = CLLocationCoordinate2DMake(-27.5, 153.5);
MapAnnotation *venue1Pin = [[MapAnnotation alloc] initWithTitle:#"1 ONE ST" Location:venue1Location];
VIEW FOR ANNOTATION DELEGATE METHOD:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
if ([annotation isKindOfClass:[MapAnnotation class]])
{
MapAnnotation *venueLocationAnnotation = (MapAnnotation *)annotation;
MKAnnotationView *venueLocationAnnotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:#"customAnnotation"];
venueLocationAnnotationView.rightCalloutAccessoryView.hidden = YES;
if (venueLocationAnnotationView == nil)
venueLocationAnnotationView = venueLocationAnnotation.annotationView;
else
venueLocationAnnotationView.annotation = annotation;
return venueLocationAnnotationView;
}
else
{
return nil;
}
}
QUESTION
How do I remove the information button from the aforementioned map annotation title view ?
You can use following statement:
You have to implement following delegate method for this:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation{
...
venue1Pin.rightCalloutAccessoryView = nil;
...
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
MKAnnotationView *annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:nil];
annotationView.canShowCallout = YES;
annotationView.rightCalloutAccessoryView.hidden=YES;
return annotationView;
}
-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
if ([annotation isKindOfClass:[MKPointAnnotation class]])
{
MKPinAnnotationView *pinView = (MKPinAnnotationView*)[mapView dequeueReusableAnnotationViewWithIdentifier:#"yourPinIdentifier"];
if (!pinView)
{
pinView.rightCalloutAccessoryView.hidden=YES;
pinView.animatesDrop = YES;
pinView.canShowCallout = YES;
}
return pinView;
}
}
In my app I am displaying a tableview which contains result of searched user from database. Now in that tableview there is one button by clicking on that user can see location of all searched users in map.when user first click on button he will be able to see all user's location with green color pin. I am displaying map in half part of view. Now When user select any particular searched user from table I want to change color of that user's pin.And if user select any other user from table previous selected user's pin color should be changed to as it was before.
I have tried this:
- (void)selectAnnotation:(id < MKAnnotation >)annotation animated:(BOOL)animated{
MKPinAnnotationView *chng=[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:nil];
chng.pinColor=MKPinAnnotationColorRed;
NSLog(#"========>selected");
}
And I am calling this method when user select particular user from table.
CLLocationCoordinate2D CurrentCoordinateSingleUser;
CurrentCoordinateSingleUser.latitude=[[singleUserPin objectAtIndex:1] doubleValue];
CurrentCoordinateSingleUser.longitude=[[singleUserPin objectAtIndex:2] doubleValue];
MapObjects *map1=[[MapObjects alloc]initwithCoordinate:CurrentCoordinateSingleUser title:[singleUserPin objectAtIndex:0] subtitle:Nil];
[userMap addAnnotation:map1];
for (id<MKAnnotation> currentAnnotation in userMap.annotations) {
if ([currentAnnotation isEqual:annotationToSelect]) {
[userMap selectAnnotation:currentAnnotation animated:FALSE];
}
}
But here selectAnnotation is not working.
UPDATE::
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
if([annotation isKindOfClass:[MKUserLocation class]])
return nil;
static NSString *identifier = #"myAnnotation";
MKPinAnnotationView * annotationView = (MKPinAnnotationView*)[userMap dequeueReusableAnnotationViewWithIdentifier:identifier];
if([annotation isEqual:map1]){
annotationView.pinColor = MKPinAnnotationColorRed;
}
else {
if (!annotationView)
{
annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
// if(fromSelectedTab==TRUE){
// annotationView.pinColor = MKPinAnnotationColorRed;
// annotationView.animatesDrop = NO;
// fromSelectedTab=FALSE;
// }
// else{
annotationView.pinColor = MKPinAnnotationColorGreen;
annotationView.animatesDrop = NO;
// }
annotationView.canShowCallout = YES;
// annotationView.dr
}
else {
annotationView.annotation = annotation;
}
}
//annotationView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
return annotationView;
}
MapObjects.h:
#interface MapObjects : NSObject<MKMapViewDelegate,MKAnnotation>
{
NSString *title,*subtitle;
CLLocationCoordinate2D coordinate;
}
#property(nonatomic,copy)NSString *title;
#property(nonatomic,copy)NSString *subtitle;
#property(nonatomic) CLLocationCoordinate2D coordinate;
-(MapObjects *)initwithCoordinate:(CLLocationCoordinate2D)coordinate title:(NSString *)title subtitle:(NSString *)subtitle;
#end
Use :
- (MKAnnotationView *)viewForAnnotation:(id <MKAnnotation>)annotation;
instead of creating a new MKPinAnnotationView. Or if it's in purpose you have to add somewhere the new annotation in your map.
Hope that will help
You should override MKMapiew method
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation (id<MKAnnotation>)annotation {
static NSString *identifier = #"PinAnnotation";
// map1 should be visible in method context
if ([annotation isEqual:map1]) {
MKPinAnnotationView *pinAnnotation = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
pinAnnotation.pinColor = MKPinAnnotationColorRed;// or any you want
return pinAnnotation;
}
return nil;
}
There you can determine specified annotation and set it desired color.
in viewDidLoad:
- (void)viewDidLoad {
[super viewDidLoad];
// There you should have selected "user" object and "users" array
...
// _annotations is declared in header
_annotations = [[NSMutableArray alloc] initWithCapacity:0];
for (UserObj *obj in users) {
CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(0.f, 0.f);
coordinate.latitude = obj.latitude;
coordinate.longitude = obj.longitude;
MapObjects *userAnnotation = [[MapObjects alloc] initWithCoordinate:coordinate title:obj.name subtitle:nil];
[map addAnnotation:userAnnotation];
[_annotations addObject:userAnnotation];
}
...
}
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKPinAnnotationView *)view {
view.pinColor = MKPinAnnotationColorRed;
}
- (void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKPinAnnotationView *)view {
view.pinColor = MKPinAnnotationColorGreen;
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation (id<MKAnnotation>)annotation {
static NSString *identifier = #"PinAnnotation";
MKPinAnnotationView *pinAnnotation = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if(!pinAnnotation) {
pinAnnotation = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
}
pinAnnotation.pinColor = MKPinAnnotationColorGreen;
return pinAnnotation;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// You should keep your annotation objects in some array too because map.annotations won't return annotation in order that we need
// After you add annotation to map, add them to NSMutableArray too, [_annotations addObject:userAnnotation];
MapObjects annotation = _annotations[indexPath.row];// not sure that it return that exact annotation
[map selectAnnotation:annotation animated:YES];
}
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
}