"CustomAnnotation is tapped" usage in MKMapView - ios

I need to put custom annotation views to mapview, and they need to be clickable. i need rather than classic callout, i will tap on the annotation and it will perform some functions.
I tried many things but still not achieved.

I found a really good implementation for you:
http://blog.asolutions.com/2010/09/building-custom-map-annotation-callouts-part-1/
there you can add buttons to calloutView with actions
or..
in mapView delegate you can customize the annotation, and the callout view:
viewForAnnotation:
Returns the annotation view associated with the specified annotation object, if any.
(MKAnnotationView *)viewForAnnotation:(id < MKAnnotation >)annotation
there is an example:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
MKAnnotationView *annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"loc"];
// Button
UIButton *button = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
button.frame = CGRectMake(0, 0, 23, 23);
annotationView.rightCalloutAccessoryView = button;
// Image and two labels
UIView *leftCAV = [[UIView alloc] initWithFrame:CGRectMake(0,0,23,23)];
[leftCAV addSubview : yourImageView];
[leftCAV addSubview : yourFirstLabel];
[leftCAV addSubview : yourSecondLabel];
annotationView.leftCalloutAccessoryView = leftCAV;
annotationView.canShowCallout = YES;
return annotationView;
}
EDITED ANSWER FOR CUSTOM PIN:
make a MKAnnotationView subclass
.h
#import <MapKit/MapKit.h>
#interface AnnotationView : MKAnnotationView
- (id)initWithAnnotation:(id <MKAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier;
#end
.m
#import "AnnotationView.h"
#implementation AnnotationView
{
NSString *identifier;
}
- (id)initWithAnnotation:(id <MKAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier];
if (self != nil)
{
CGRect frame = self.frame;
frame.size = CGSizeMake(78.0, 35.0);
self.frame = frame;
self.backgroundColor = [UIColor clearColor];
self.centerOffset = CGPointMake(0, 0);
}
return self;
}
-(void) drawRect:(CGRect)rect
{
[[UIImage imageNamed:#"yourImage"]drawInRect:CGRectMake(0, 0, 78, 35)];
}
#end
then in the mapview delegate:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
if ([annotation isKindOfClass:[MKUserLocation class]]) {
return nil;
}
if ([annotation isKindOfClass:[Annotation class]]) {
NSString* annotationIdentifier = #"yourAnnotation";
AnnotationView* customAnnotationView = (AnnotationView*)[mapView dequeueReusableAnnotationViewWithIdentifier:annotationIdentifier];
if (!customAnnotationView) {
customAnnotationView = [[AnnotationView alloc]initWithAnnotation:annotation reuseIdentifier:annotationIdentifier];
customAnnotationView.canShowCallout = YES;
}
else{
customAnnotationView.annotation= annotation;
}
return customAnnotationView;
}
return nil;
}
Annotation class is my custom annotation class:
#interface Annotation : NSObject <MKAnnotation>
I hope this helps

There is a method on the mapViewDelegate: viewForMapView. In this method, you return a view. This view can be completely customized by you, e.g. you can add gestureregognizers to it to handle additional gestures beside tapping.

Related

Custom annotationview not displaying in MKMapView in iOS 12

I have a custom annotation view class which is like
#interface MyAnnotationView : MKAnnotationView
#property (nonatomic, strong) UIImageView *imageView;
#end
and then I use it in the MKMapView like :
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
MKAnnotationView *annotationView = nil;
MyAnnotationView *view = (MyAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:MyAnnotationViewIdentifier];
if (view == nil) {
view = [[MyAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:MyAnnotationViewIdentifier];
}
view.imageView.image = myImage;
annotationView = view;
return annotationView;
}
This code works as expected till iOS 11, but in iOS 12 I dont see the image anymore. What am I missing? Any tips appreciated.

Storing Data in Annotation and Displaying on a Custom Callout but Data Changes to the Most Recent Update

So, I've created a custom callout that appears over the selected annotation. Everything works exactly how I want--the only problem is after I've dropped another pin of the same type, then all the past pins will display the same information. For example, I may have a pin drop that displays in the callout "McDonalds" and the next pin that drops displays "Starbucks". Now all my previously dropped pins will display "Starbucks".
I'm currently attempting to store information in the annotation's subtitle property and then displaying that through a formatted string onto my custom UIView's Label...It works but I need the annotation's information to never change. There must be something I'm missing or do not understand. Any help will be much appreciated.
I've posted all the code I believe to be relevant below. Thank you!
Custom Callout.h
#import <UIKit/UIKit.h>
#interface PinView : UIView
{
UIView *view;
UILabel *theValueLabel;
}
#property (nonatomic, retain) IBOutlet UIView *view;
#property (nonatomic, retain) IBOutlet UILabel *theValueLabel;
#end
Custom Callout.m
#import "PinView.h"
#implementation PinView
#synthesize theValueLabel;
#synthesize view;
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
UIView *nib = [[[UINib nibWithNibName:#"customView" bundle:nil] instantiateWithOwner:self options:nil] objectAtIndex:0];
[self addSubview:nib];
}
return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
if (self.subviews.count == 0) {
UIView *nib = [[[UINib nibWithNibName:#"customView" bundle:nil] instantiateWithOwner:self options:nil] objectAtIndex:0];
[self addSubview:nib];
}
}
return self;
}
- (void)awakeFromNib {
[super awakeFromNib];
[self setup];
}
- (void)setup {
self.theValueLabel.text = #"foo";
}
in my main View Controller.m
#interface BreadTrailViewController ()<CLLocationManagerDelegate, MKMapViewDelegate, MFMailComposeViewControllerDelegate>
{
PinView *aView
MKPointAnnotation *Pin1;
}
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
CLLocation *location = [locations lastObject];
if (location != nil)
{
Pin1 = [[MKPointAnnotation alloc] init];
Pin1.title = #"Venue";
Pin1.subtitle = [NSString stringWithFormat:#"Venue Name: %#\n%f,%f\nAddress: %#",revGeocodeVenue, lat, lng, revGeocodeAddress];
Pin1.coordinate = location.coordinate;
[self.mapView addAnnotation:Pin1];
}
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
// If it's the user location, just return nil.
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil;
// Handle any custom annotations.
if ([annotation isKindOfClass:[MKPointAnnotation class]])
{
MKPinAnnotationView *pinView = (MKPinAnnotationView*)[mapView dequeueReusableAnnotationViewWithIdentifier:#"CustomPinAnnotationView"];
if (!pinView)
{
pinView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"CustomPinAnnotationView"];
pinView.canShowCallout = NO;
} else {
pinView.annotation = annotation;
}
if ([[annotation title] containsString:#"Venue"])
{
pinView.pinTintColor = [UIColor greenColor];
}
return pinView;
}
return nil;
}
-(void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
{
aView = [[PinView alloc] initWithFrame:CGRectMake( 0, 0, 300, 350)];
aView.layer.cornerRadius = 20;
aView.layer.masksToBounds = YES;
aView.center = CGPointMake(view.bounds.size.width*0.5f, -aView.bounds.size.height*0.35f);
//Using the Pin's subtitle to store the data string and display on PinView
if ([[view.annotation title] containsString:#"Venue"])
{
aView.theValueLabel.text = Pin1.subtitle;
}
[view addSubview:aView];
}
-(void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)view
{
[aView removeFromSuperview];
}
I found the problem! I was using Pin1.subtitle when what I should be using is view.annotation.subtitle--everything is working perfectly now. I hope this helps someone else!
-(void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
{
aView = [[PinView alloc] initWithFrame:CGRectMake( 0, 0, 300, 350)];
aView.layer.cornerRadius = 20;
aView.layer.masksToBounds = YES;
aView.center = CGPointMake(view.bounds.size.width*0.5f, -aView.bounds.size.height*0.35f);
//Using the Pin's subtitle to store the data string and display on PinView
if ([[view.annotation title] containsString:#"Venue"])
{
aView.theValueLabel.text = view.annotation.subtitle;
}
[view addSubview:aView];
}

MKAnnotation pin color update

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];
}

didSelectAnnotationView doesn't call on custom MKAnnotationView

I have set delegate on mapView
I've set setCanShowCallout: to YES
Code from my custom MKAnnotationView:
- (id)initWithAnnotation:(id<MKAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier{
self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier];
if (self) {
self.canShowCallout = YES;
_pinView = [[CustomPinView alloc] init];
[self addSubview:_pinView];
}
return self;
}
Code from my VC:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation{
MapAnnotationView *pinView = nil;
pinView = (MapAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:#"lol"];
if (pinView == nil){
pinView = [[MapAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"lol"];
}
return pinView;
}
What am I doing wrong, pls help me.
Allright, I'll do it by myself.
You should set image to your custom MKAnnotationView with size of subviews.
You should set canShowCallout = YES only if your MKPointAnnotation has description string (not nil, or #""), otherwise you should set canShowCallout = NO
After that method didSelectAnnotationView will be called. Hope it helps for someone.

Why can not add annotations on mapView?

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];
}
}

Resources