Callout opening wrong view after zoom in iOS7 - ios

Everything is working fine in my app except for one thing: after zooming in and zooming back out, to see the whole map, some callouts open the wrong detailview.
I don't know if I'm missing some code or else.
Using Xcode 5.1.1 for iOS7.
This is what I've got at the moment:
Annotation.h
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface Annotation: NSObject <MKAnnotation>
#property (nonatomic, assign) CLLocationCoordinate2D coordinate;
#property (nonatomic, copy) NSString *title;
#property (nonatomic, copy) NSString *subtitle;
#end
Annotation.m
#import "Annotation.h"
#implementation Annotation
#synthesize coordinate,title,subtitle;
#end
MapView.h
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#interface Nameofthemap : UIViewController <MKMapViewDelegate>
#property (strong, nonatomic) IBOutlet MKMapView *Nameofthemap;
#end
MapView.m
#import "MapView.h"
#import "Annotation.h"
#import "InfoViewController.h"
#import "InfoTwoViewController.h"
#interface MapView ()
#property (nonatomic, strong) IBOutlet InfoViewController *InfoViewController;
#property (nonatomic, strong) IBOutlet InfoTwoViewController *InfoTwoViewController;
#end
#define PLACE1_LATITUDE 43.777130;
#define PLACE2_LONGITUDE 10.790018;
#define PLACE2_LATITUDE 43.81471237;
#define PLACE2_LONGITUDE 10.67472765;
#implementation MapView
- (IBAction)changeMapType:(id)sender {
if (_MapView.mapType == MKMapTypeHybrid)
_MapView.mapType = MKMapTypeStandard;
else
_MapView.mapType = MKMapTypeHybrid;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self gotoLocation];
_MapView.showsUserLocation = YES;
}
- (void)gotoLocation
{
MKCoordinateRegion newRegion;
newRegion.center.latitude = PLACE1_LATITUDE;
newRegion.center.longitude = PLACE2_LONGITUDE;
newRegion.span.latitudeDelta = 0.25f;
newRegion.span.longitudeDelta = 0.25f;
[self.MapView setRegion:newRegion animated:YES];
NSMutableArray * locations = [[NSMutableArray alloc] init];
CLLocationCoordinate2D location;
Annotation *myAnn;
Annotation *myAnn2;
//Place1 annotation
myAnn = [[Annotation alloc] init];
location.latitude = PLACE1_LATITUDE;
location.longitude = PLACE1_LONGITUDE;
myAnn.coordinate = location;
myAnn.title = #"Name of the place";
myAnn.subtitle = #"Details";
[locations addObject:myAnn];
//Place2 annotation
myAnn2 = [[Annotation alloc] init];
location.latitude = PLACE2_LATITUDE;
location.longitude = PLACE2_LONGITUDE;
myAnn2.coordinate = location;
myAnn2.title = #"Name of place two";
myAnn2.subtitle = #"Details";
[locations addObject:myAnn2];
[self->_MapView addAnnotations:locations];
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control{
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)myAnn {
if ([myAnn isKindOfClass:[MKUserLocation class]])
{
((MKUserLocation *)myAnn).title = #"Your position";
return nil;
}
MKPinAnnotationView *pinView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:#"pinView"];
if (!pinView) {
pinView = [[MKPinAnnotationView alloc] initWithAnnotation:myAnn reuseIdentifier:#"pinView"];
pinView.pinColor = MKPinAnnotationColorRed;
pinView.canShowCallout = YES;
UIButton *rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
if ([[myAnn title] isEqualToString:#"Name of the place"]){
[rightButton addTarget:self action:#selector(myAnnClicked:)forControlEvents:UIControlEventTouchUpInside];
}
if ([[myAnn title] isEqualToString:#"Name of place two"]){
[rightButton addTarget:self action:#selector(myAnn2Clicked:)forControlEvents:UIControlEventTouchUpInside];
}
pinView.rightCalloutAccessoryView = rightButton;
}
return pinView;
}
-(IBAction)myAnnClicked:(id)sender
{
InfoViewController *info = [[InfoViewController alloc]init];
[self.navigationController pushViewController:info animated:YES];
}
-(IBAction)myAnn2Clicked:(id)sender
{
InfoTwoController *info2 = [[InfoTwoController alloc]init];
[self.navigationController pushViewController:info2 animated:YES];
}
#end

It's an annotation view re-use issue.
In viewForAnnotation, the button targets are only being set when creating a view (if dequeueReusableAnnotationViewWithIdentifier returns nil).
But if dequeueReusableAnnotationViewWithIdentifier returns a previously-used view, the button target is still whatever was set for the annotation that used the view before.
That previous annotation may not be the same as the current annotation.
So it's possible for annotation "two" to re-use a view that was originally created for annotation "one" and tapping on the already-created button shows the info for "one" instead of "two".
To fix this, two things should be done:
If dequeueReusableAnnotationViewWithIdentifier returns a view (if pinView is not nil), the code must update the view's annotation property to the current annotation.
The button target must be set whether a new view is being created or a dequeued view is being re-used. The easiest way to do this is to move the button creation/setting after the main if and just before the return.
The updated viewForAnnotation would look like this:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)myAnn {
if ([myAnn isKindOfClass:[MKUserLocation class]])
{
((MKUserLocation *)myAnn).title = #"Your position";
return nil;
}
MKPinAnnotationView *pinView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:#"pinView"];
if (!pinView) {
pinView = [[MKPinAnnotationView alloc] initWithAnnotation:myAnn reuseIdentifier:#"pinView"];
pinView.pinColor = MKPinAnnotationColorRed;
pinView.canShowCallout = YES;
}
else
{
//1. Re-using a view, update which annotation it's being used for now
pinView.annotation = myAnn;
}
//2. Now pinView is either a new view or re-used view.
//Set its button target based on current annotation...
UIButton *rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
if ([[myAnn title] isEqualToString:#"Name of the place"]){
[rightButton addTarget:self action:#selector(myAnnClicked:)forControlEvents:UIControlEventTouchUpInside];
}
if ([[myAnn title] isEqualToString:#"Name of place two"]){
[rightButton addTarget:self action:#selector(myAnn2Clicked:)forControlEvents:UIControlEventTouchUpInside];
}
pinView.rightCalloutAccessoryView = rightButton;
return pinView;
}
By the way, instead of creating separate methods for each annotation (which can get tedious), use the map view's calloutAccessoryControlTapped delegate method instead.
In fact, right now, the map view is calling both your custom methods and the calloutAccessoryControlTapped delegate method (in which there's no code currently).
In the delegate method, the annotation tapped is accessible via view.annotation.
So in viewForAnnotation, you would just do this:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)myAnn {
if ([myAnn isKindOfClass:[MKUserLocation class]])
{
((MKUserLocation *)myAnn).title = #"Your position";
return nil;
}
MKPinAnnotationView *pinView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:#"pinView"];
if (!pinView) {
pinView = [[MKPinAnnotationView alloc] initWithAnnotation:myAnn reuseIdentifier:#"pinView"];
pinView.pinColor = MKPinAnnotationColorRed;
pinView.canShowCallout = YES;
pinView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
}
else
{
pinView.annotation = myAnn;
}
return pinView;
}
Then in the calloutAccessoryControlTapped delegate method, you can do something like this:
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control{
if ([view.annotation isKindOfClass:[Annotation class]])
{
Annotation *myAnn = (Annotation *)view.annotation;
id vcToPush = nil;
if ([[myAnn title] isEqualToString:#"Name of the place"]) {
vcToPush = [[InfoViewController alloc]init];
}
else if ([[myAnn title] isEqualToString:#"Name of place two"]) {
vcToPush = [[InfoTwoController alloc]init];
}
[self.navigationController pushViewController:vcToPush animated:YES];
}
}
Then remove the myAnnClicked and myAnn2Clicked methods.
You would also be much better off creating a generic "Info" view controller instead of a separate one for each annotation.
Some other unrelated things:
Don't put a semi-colon at the end of the #define lines
You've defined PLACE2_LONGITUDE twice
newRegion.center is using PLACE2_LONGITUDE instead of PLACE1_LONGITUDE

Related

MKAnnotationView image changing issue

I've got an MKMapView loaded with plenty of custom annotation (800 circa).
When I drag on the map and I return to an annotation, it's image has changed with another one. For me it's seems like a cache issue.
Pin before dragging
Pin after dragging
SuperClass header
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface MapAnnotation : NSObject <MKAnnotation>
#property (nonatomic, copy) NSString *title;
#property NSString * pinImageName;
#property (nonatomic) CLLocationCoordinate2D coordinate;
- (id)initWithCoordinate:(CLLocationCoordinate2D)coordinate title:(NSString *)title pinImageName:(NSString *)pinImageName;
- (MKAnnotationView *)getAnnotationView;
#end
SubClass (the one that creates the issue) header
#import "MapAnnotation.h"
#import "Company.h"
#interface CompanyAnnotation : MapAnnotation
#property Company *company;
#property UIImage *pinImage;
- (id)initWithCoordinate:(CLLocationCoordinate2D)coordinate title:(NSString *)title pinImage:(UIImage *)pinImage;
- (MKAnnotationView *)getAnnotationView;
#end
viewForAnnotation delegate method
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id)annotation {
if (annotation == mapView.userLocation){
return nil;
}
MapAnnotation *location = [MapAnnotation new];
MKAnnotationView *annotationView = [MKAnnotationView new];
if ([annotation isKindOfClass:[CompanyAnnotation class]]) {
location = (CompanyAnnotation *)annotation;
annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:#"companyAnnotation"];
}
if (annotationView == nil) {
annotationView = [location getAnnotationView];
} else {
annotationView.annotation = annotation;
}
return annotationView;
}
getAnnotationView method
- (MKAnnotationView *)getAnnotationView {
MKAnnotationView * annotationView = [[MKAnnotationView alloc] initWithAnnotation:self reuseIdentifier:#"companyAnnotation"];
[annotationView setEnabled:YES];
[annotationView setCanShowCallout:YES];
[annotationView setContentMode:UIViewContentModeScaleAspectFit];
[annotationView setImage:self.pinImage];
[annotationView setFrame:CGRectMake(0, 0, 28, 44)];
[annotationView setRightCalloutAccessoryView:[UIButton buttonWithType:UIButtonTypeDetailDisclosure]];
return annotationView;
}
The dequeue is not being handled properly in viewForAnnotation because when dequeueReusableAnnotationViewWithIdentifier returns a previously-used view (when annotationView is not nil), the code is only updating that view's annotation property:
if (annotationView == nil) {
annotationView = [location getAnnotationView];
} else {
annotationView.annotation = annotation;
}
But the annotation view's image is not updated -- in a dequeued view, the image will be set to the one associated with the annotation the view was originally created for (when getAnnotationView was called).
So now the view appears at the new annotation's coordinates but the image is still from the previous annotation the view was used for.
There are various ways to fix this such as creating a proper subclass of MKAnnotationView that monitors changes to its annotation property and automatically updates all other properties associated with an annotation.
With the existing code, a simple way to fix it is to separate out the annotation-specific property changes into a separate method that can be called both when the view is created and when its annotation property is updated.
For example, in CompanyAnnotation, create a method like this:
-(void)configureView:(MKAnnotationView *)av
{
av.image = self.pinImage;
}
Then change getAnnotationView to:
- (MKAnnotationView *)getAnnotationView {
MKAnnotationView * annotationView = [[MKAnnotationView alloc] initWithAnnotation:self reuseIdentifier:#"companyAnnotation"];
//set properties here that are not specific to an annotation...
[annotationView setEnabled:YES];
[annotationView setCanShowCallout:YES];
[annotationView setContentMode:UIViewContentModeScaleAspectFit];
//[annotationView setImage:self.pinImage];
[annotationView setFrame:CGRectMake(0, 0, 28, 44)];
[annotationView setRightCalloutAccessoryView:[UIButton buttonWithType:UIButtonTypeDetailDisclosure]];
//set properties that are specific to an annotation...
[self configureView:annotationView];
return annotationView;
}
Finally in viewForAnnotation:
if (annotationView == nil) {
annotationView = [location getAnnotationView];
} else {
annotationView.annotation = annotation;
if ([annotation isKindOfClass:[CompanyAnnotation class]]) {
//update image, etc...
[annotation configureView:annotationView];
}
}
Note that MapAnnotation will have the same issue with the pinImageName property.

how send Core Data object from MapView to DetailView

I have a list of gyms with all its properties in Core Data, I get this gyms with an NSFetchRequest and pass its to a NSArray, then with a For in and this array, I get to put the pins in the MapView, I do it with de next code (welcome suggestions for improvement this):
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:YES];
NSFetchRequest *reqInst = [NSFetchRequest fetchRequestWithEntityName:[KMTInstalacion entityName]];
reqInst.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:KMTInstalacionAttributes.tipoInstalacion_ES ascending:NO]];
KMTAppDelegate *appDelegate = (KMTAppDelegate *)[[UIApplication sharedApplication]delegate];
NSError *error;
NSArray *arrayInstalaciones = [appDelegate.model.context executeFetchRequest:reqInst error:&error];
NSLog(#"Las instalaciones encontradas en arrayInstalaciones son: %lu", (unsigned long)[arrayInstalaciones count]);
for (KMTInstalacion *todasInstalaciones in arrayInstalaciones) {
CLLocationCoordinate2D coordPunto1 = CLLocationCoordinate2DMake(todasInstalaciones.coorLatitudValue, todasInstalaciones.coorLongitudValue);
MKPointAnnotation *anotacion = [[MKPointAnnotation alloc]init];
[anotacion setCoordinate:coordPunto1];
[anotacion setTitle:todasInstalaciones.nombre_ES];
[anotacion setSubtitle:todasInstalaciones.direccion_ES];
[self.mapView addAnnotation:anotacion];
}}
- (MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation{
MKPinAnnotationView *pinView = [[MKPinAnnotationView alloc]initWithAnnotation:annotation reuseIdentifier:#"curr"];
pinView.animatesDrop = NO;
pinView.pinColor = MKPinAnnotationColorRed;
pinView.canShowCallout = YES;
pinView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
return pinView;
-(void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control{
KMTMiAnotacion *anotacion = view.annotation;
KMTDetailVC *detalle = [[KMTDetailVC allc]init];
detalle.title = anotacion.title
detalle.nombreInstalacionSeleccionada = anotacion.instalacion;
[self performSegueWithIdentifier:#"deMapa" sender:self];
}
KMTMiAnotacion.h
#interface KMTMiAnotacion : NSObject <MKAnnotation>{
NSString *title;
NSString *subTitle;
KMTInstalacion *instalacion;
}
#property (nonatomic, copy) NSString *title;
#property (nonatomic, copy) NSString *subTitle;
#property (nonatomic, copy) KMTInstalacion *instalacion;
#end
When I tap on one of the pinView shows me the name and address of the gym with the button to the right.
Now what I want is when you tap on the pinView button, take you to the DetailView view and send full gym object (KMTInstalacion) corresponding pinView you've pressed, not just the title or subtitle, since each gym has about 50 properties (photos, schedules, etc ...)
Any idea how to do this?
Make a property of the gym object in your DetailViewController's .h file. Also create a custom annotation (subclass of MKAnnotation) with the same property.
#property (nonatomic, strong) KMTInstalacion *gymObj;
When setting the annotation, assign the gymObj to it's property.
Then implement this delegate method of MKMapView in your Map Controllers .m file:
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view
calloutAccessoryControlTapped:(UIControl *)control
{
YourAnnotation *annView = view.annotation;
detailedViewOfList *detailView = [[DetailedViewOfList alloc]init];
detailView.gymObj = annotation.gymObj;
// push view modally or, however you want
}
You can use below code for sample how u can send details to other view. As per your need you to send entire object to other view...!
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view
calloutAccessoryControlTapped:(UIControl *)control
{
annotation *annView = view.annotation;
DetailedViewController *detailVC = [[DetailedViewController alloc] initWithNibName:#"DetailedViewController" bundle:nil];
detailVC.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
detailVC.address = annView.address;
detailVC.name = annView.name;
[self presentModalViewController:detailVC animated:YES];
}
Hope it helps you..
First in your annotaion view delegat make a button to go in detail view like bellow:
-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation{
MKPinAnnotationView *mypin = [[MKPinAnnotationView alloc]initWithAnnotation:annotation reuseIdentifier:#"current"];
mypin.pinColor = MKPinAnnotationColorPurple;
mypin.backgroundColor = [UIColor clearColor];
UIButton *goToDetail = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
mypin.rightCalloutAccessoryView = myBtn;
mypin.draggable = NO;
mypin.highlighted = YES;
mypin.animatesDrop = TRUE;
mypin.canShowCallout = YES;
return mypin;
}
Now use the following delegate whenever the button in annotationView will get tapped the following delegate will be called from where you can easly get which particular annotaion's button is tapped
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view
calloutAccessoryControlTapped:(UIControl *)control
{
annotation *annView = view.annotation;
detailedViewOfList *detailView = [[detailedViewOfList alloc]init];
detailView.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
detailView.address = annView.address;
detailView.phoneNumber = annView.phonenumber;
[self presentModalViewController:detailView animated:YES];
OR
[self.navigationController pushViewController:detailView animated:YES];
}
here annotaion is a class importing MKAnnotaion.h and address and phonenumber are properties of annotaion class you can make many more while the address and phoneNumber properties of detailView class are strong. So that you can pass values. Hope this will help you!

How can I add different images instead of pins in MKMapView?

I've been working on an app that has an MKMapView and I want to customize the pins with different images. I've already did that but with one image only, now I need to make a pin show an image and the other pin show another image. How can I do this? If helps, here's my code:
-(MKAnnotationView *)mapView:(MKMapView *)mV viewForAnnotation:(id <MKAnnotation>)annotation{
MKAnnotationView *pinView = nil;
if(annotation != _mapView.userLocation)
{
static NSString *defaultPinID;
pinView = (MKAnnotationView *)[_mapView dequeueReusableAnnotationViewWithIdentifier:defaultPinID];
if ( pinView == nil )
pinView = [[MKAnnotationView alloc]
initWithAnnotation:annotation reuseIdentifier:defaultPinID];
pinView.canShowCallout = YES;
pinView.image = [UIImage imageNamed:#"image1.jpg"];
}
else {
[_mapView.userLocation setTitle:#"Your Location"];
}
return pinView; }
I need the second and the third to show the same image but the first a different one
It follows like this:
- (void)viewWillAppear:(BOOL)animated {CLLocationCoordinate2D First; First.latitude = -12.098970; First.longitude = -77.034531; MKPointAnnotation *annotationPoint = [[MKPointAnnotation alloc] init]; annotationPoint.coordinate = First; annotationPoint.title = #"First"; annotationPoint.subtitle = #"Subtitle1"; [_mapView addAnnotation:annotationPoint];
CLLocationCoordinate2D Second; Second.latitude = -12.098299; Second.longitude = -77.068364; MKPointAnnotation *annotationPoint2 = [[MKPointAnnotation alloc] init]; annotationPoint2.coordinate = Second; annotationPoint2.title = #"Second"; annotationPoint2.subtitle = #"Subtitle2"; [_mapView addAnnotation:annotationPoint2];
CLLocationCoordinate2D Third; Third.latitude = -12.125888; Third.longitude = -77.023346; MKPointAnnotation *annotationPoint3 = [[MKPointAnnotation alloc] init]; annotationPoint3.coordinate = Third; annotationPoint3.title = #"Third"; annotationPoint3.subtitle = #"Subtitle3"; [_mapView addAnnotation:annotationPoint3];}
Make sure you have MapKit.framework and CoreLocation.framework in your project.
My custom pin images are 39 high by 32 wide. Have not tried other sizes but feel free to experiment. My 2 pin images are called pin1.png and pin2.png
Make sure you have your images named correctly to match what is in your code.
In my example I am not using current location but rather a static custom location (thought The Bahamas would be nice for this example). In your project you would of course you the Location Manager to get a user's current location.
I have tested my example and have successfully dropped 2 pins on the map with each pin having its own custom image.
It's not the cleanest code but I only had limited time to write it.
Here is the code for ViewController.h
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#import <CoreLocation/CoreLocation.h>
#interface ViewController : UIViewController <CLLocationManagerDelegate, MKMapViewDelegate>
#end
Here is the code for ViewController.h
#import "ViewController.h"
#import "MyAnnotation.h"
#import <MapKit/MapKit.h>
#interface ViewController ()
#property (strong, nonatomic) IBOutlet MKMapView *myMapView;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// setup the map view, delegate and current location
[self.myMapView setDelegate:self];
self.myMapView.mapType = MKMapTypeStandard;
CLLocationCoordinate2D myLocation = CLLocationCoordinate2DMake(25.085130,-77.331428);
[self.myMapView setCenterCoordinate:myLocation];
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(myLocation, 2000, 2000);
region.center = self.myMapView.centerCoordinate;
self.myMapView.showsUserLocation = YES;
[self.myMapView setRegion:region animated:YES];
[self dropPins];
}
-(void)dropPins {
NSMutableArray *annotationArray = [[NSMutableArray alloc] init];
CLLocationCoordinate2D location1 = CLLocationCoordinate2DMake(25.085130, -77.331428);
MyAnnotation *annotation1 = [[MyAnnotation alloc] initWithCoordinates:location1 image:#"pin1.png"];
[annotationArray addObject:annotation1];
[self.myMapView addAnnotations:annotationArray];
[annotationArray removeAllObjects];
CLLocationCoordinate2D location2 = CLLocationCoordinate2DMake(25.085130, -77.336428);
MyAnnotation *annotation2 = [[MyAnnotation alloc] initWithCoordinates:location2 image:#"pin2.png"];
[annotationArray addObject:annotation2];
[self.myMapView addAnnotations:annotationArray];
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
static NSString *identifier = #"MyLocation";
if ([annotation isKindOfClass:[MyAnnotation class]])
{
MKPinAnnotationView *annotationView = (MKPinAnnotationView *)[self.myMapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if (annotationView == nil)
{
annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
} else
{
annotationView.annotation = annotation;
}
annotationView.enabled = YES;
annotationView.canShowCallout = NO;
if([[(MyAnnotation *)annotationView.annotation image] isEqualToString:#"pin1.png"])
annotationView.image = [UIImage imageNamed:#"pin1.png"];
if([[(MyAnnotation *)annotationView.annotation image] isEqualToString:#"pin2.png"])
annotationView.image = [UIImage imageNamed:#"pin2.png"];
return annotationView;
}
return nil;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Here is the code for MyAnnotation.h
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface MyAnnotation : NSObject <MKAnnotation>
#property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
#property (nonatomic, copy, readonly) NSString *image;
-(id)initWithCoordinates:(CLLocationCoordinate2D) paramCoordinates
image:(NSString *) paramImage;
#end
Here is the code for MyAnnotation.m
#import "MyAnnotation.h"
#implementation MyAnnotation
-(id)initWithCoordinates:(CLLocationCoordinate2D)paramCoordinates
image:(NSString *)paramImage
{
self = [super init];
if(self != nil)
{
_coordinate = paramCoordinates;
_image = paramImage;
}
return (self);
}
#end

didSelectAnnotationView method creates an error

In my app, I have a mapView and few other components.
I have changed the view of annotation into my own icons, the icons gets displayed in the specific location of the the latitude and longitude.
But, when I click and hold the icons, it automatically gets converted into the anotations.
Please help me.
This is my map while loading
It becomes as such When I click and hold the annotations
didUpdateUserLocation
{
CLLocationCoordinate2D first;
first.latitude=13.040202;
first.longitude=80.24298;
myAnnotation.coordinate=first;
[locations addObject:myAnnotation];
[self.mapView addAnnotations:locations];
}
viewForAnnotation:
{
static NSString *identifier = #"Wifintech";
pinView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if ( pinView == nil )
pinView = [[MKPinAnnotationView alloc]initWithAnnotation:annotation reuseIdentifier:identifier];
pinView.image = [UIImage imageNamed:#"car-side.png"];
pinView.canShowCallout = YES;
}
didSelectAnnotationView
{
float latitude = [[view annotation ] coordinate].latitude;
float longitude = [[view annotation ] coordinate].longitude;
title_value=[[view annotation] title];
NSString * subtitle_val =[[view annotation] subtitle];
title_para.text=[NSString stringWithFormat:#"%#, %#",title_value,subtitle_val];
latitude_value.text=[NSString stringWithFormat:#"%f",latitude];
longitude_value.text=[NSString stringWithFormat:#"%f",longitude];
}
When using your own images, you need to create an MKAnnotationView not an MKPinAnnotationView. Please try searching on SO for answers before posting questions (it will save everyone including you a lot of time) .
MKAnnotationView ClassReference
Try returning a MKAnnotationView instead of MKPinAnnotationView from
-mapView:viewForAnnotation:
Most likely the subclass overrides some method that are tracking the selected-state of the view and adjusting the image. I suspect this will not be the case when using MKAnnotationView.
I think you need to take a close look at the following documentation.
Also, maybe make sure that the view in didSelect.. method is actually a pinAnnotionView and that the image is what you think it is.
From the documentation on MKAnnotationView:
Annotation views support the concept of a selection state, which determines whether the view is unselected, selected, or selected and displaying a standard callout view. The user toggles between the selection states through interactions with the annotation view. In the unselected state, the annotation view is displayed but not highlighted. In the selected state, the annotation is highlighted but the callout is not displayed. And finally, the annotation can be displayed both with a highlight and a callout. The callout view displays additional information such as a title string and controls for viewing more information. The title information is provided by the annotation object but your annotation view is responsible for providing any custom controls. For more information, see the subclassing notes.
This is my code
#import "FirstViewController.h"
#import "LocationObject.h"
#define THE_SPAN 0.01f;
#interface FirstViewController ()
#property (weak, nonatomic) IBOutlet MKMapView *mapView;
#property (weak, nonatomic) IBOutlet UITextField *latitude_value;
#property (weak, nonatomic) IBOutlet UITextField *longitude_value;
#property (copy, nonatomic) NSString *title_value;
#property(nonatomic,assign) CLLocationCoordinate2D myCoordinate;
#property(nonatomic,copy) NSMutableArray *locations;
#property(nonatomic,retain) MKPointAnnotation * myAnnotation;
#property (readwrite) int tag;
#property(retain) MKPinAnnotationView *pinView;
#property (weak, nonatomic) IBOutlet UITextView *title_para;
#property(assign) CLLocationCoordinate2D *coordsArray;
#property(nonatomic,assign) MKPolyline * routeLine;
#end
#implementation FirstViewController
#synthesize latitude_value;
#synthesize longitude_value;
#synthesize title_value;
#synthesize myCoordinate;
#synthesize mapView;
#synthesize locations;
#synthesize myAnnotation;
#synthesize tag;
#synthesize pinView;
#synthesize title_para;
#synthesize title;
#synthesize coordsArray;
#synthesize routeLine;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)viewDidUnload
{
[self setMapView:nil];
[self setLatitude_value:nil];
[self setLongitude_value:nil];
[self setLatitude_value:nil];
[self setLongitude_value:nil];
[self setTitle_value:nil];
[self setTitle_para:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay {
MKPolylineView *polyLineView = [[MKPolylineView alloc] initWithPolyline:routeLine];
polyLineView.fillColor = [UIColor blueColor];
polyLineView.strokeColor = [UIColor redColor];
polyLineView.lineWidth = 2;
return polyLineView;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
//map view : region nad annotation
#pragma mark map view delegate methods
-(void) mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
//map region and center location
MKCoordinateRegion myRegion;
CLLocationCoordinate2D center;
center.latitude=13.040223;
center.longitude=80.240995;
MKCoordinateSpan span;
span.latitudeDelta=THE_SPAN;
span.longitudeDelta=THE_SPAN;
myRegion=MKCoordinateRegionMake(center, span);
MKCoordinateRegion adjusted_region=[self.mapView regionThatFits:myRegion];
[self.mapView setRegion:adjusted_region animated:YES];
locations=[[NSMutableArray alloc]init];
//first point
CLLocationCoordinate2D first;
first.latitude=13.040202;
first.longitude=80.24298;
myAnnotation = [[MKPointAnnotation alloc]init];
myAnnotation.coordinate=first;
myAnnotation.title=#"Wifin Technology";
myAnnotation.subtitle=#"Globus";
[locations addObject:myAnnotation];
//second point
CLLocationCoordinate2D second;
second.latitude=13.0406527;
second.longitude=80.2437427;
myAnnotation= [[MKPointAnnotation alloc]init];
myAnnotation.coordinate=second;
myAnnotation.title=#"The Residency Towers";
myAnnotation.subtitle=#"Chennai";
[locations addObject:myAnnotation];
//third point
CLLocationCoordinate2D third;
third.latitude=13.040202;
third.longitude=80.240191;
myAnnotation= [[MKPointAnnotation alloc]init];
myAnnotation.coordinate=third;
myAnnotation.title=#"Rado Cool Zone";
myAnnotation.subtitle=#"Chennai";
[locations addObject:myAnnotation];
[self.mapView addAnnotations:locations];
title_para.text=[[NSString alloc]initWithString:#"The position displayed is Pondy Bazaar"];
coordsArray = malloc(sizeof(CLLocationCoordinate2D) * locations.count);
int i = 0;
for (CLLocation *loc in locations) {
coordsArray[i] = loc.coordinate;
i++;
}
routeLine = [MKPolyline polylineWithCoordinates:coordsArray
count:locations.count];
[mapView addOverlay:routeLine];
self.longitude_value.text=[NSString stringWithFormat:#"%f",center.longitude];
self.latitude_value.text=[NSString stringWithFormat:#"%f",center.latitude];
}
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
{
float latitude = [[view annotation ] coordinate].latitude;
float longitude = [[view annotation ] coordinate].longitude;
title_value=[[view annotation] title];
NSString * subtitle_val =[[view annotation] subtitle];
title_para.text=[NSString stringWithFormat:#"%#, %#",title_value,subtitle_val];
latitude_value.text=[NSString stringWithFormat:#"%f",latitude];
longitude_value.text=[NSString stringWithFormat:#"%f",longitude];
}
//Annotation view: icons
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id < MKAnnotation >)annotation
{
pinView = nil;
static NSString *identifier = #"Wifintech";
pinView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if ( pinView == nil )
pinView = [[MKPinAnnotationView alloc]
initWithAnnotation:annotation reuseIdentifier:identifier];
if([[annotation title] isEqualToString:#"Wifin Technology"])
{
pinView.image = [UIImage imageNamed:#"car-side.png"];
pinView.canShowCallout = YES;
}
else if ([[annotation title] isEqualToString:#"The Residency Towers"])
{
pinView.image=[UIImage imageNamed:#"lorry-side.png"];
pinView.canShowCallout = YES;
}
else if([[annotation title] isEqualToString:#"Rado Cool Zone"])
{
//pinView.pinColor = MKPinAnnotationColorGreen;
pinView.canShowCallout = YES;
//pinView.animatesDrop = YES;
pinView.image = [UIImage imageNamed:#"car.png"];
}
else {
NSLog(#"Nothing");
}
return pinView;
}
#end

custom data variables in mkannotation

I have a map view controller (UIViewController, MKMapView), with its delegate (HCIResultMapViewController).
I wish to have following functionality in this part.
1). I wish to use my custom made NSObject , so that I can associate others details along with the basic entities like title, subtitle etc.
Hence according to my needs I coded as following
In HCIResultMapViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
_houseList = [[_resultList objectForKey:#"result"] objectForKey:#"listings"];
// NSLog([_houseList description]);
int i = 0;
for (NSDictionary *house in _houseList) {
HCIAnnotationViewController *annotation = [[HCIAnnotationViewController alloc]
initwithHouse:house];
[_mapView addAnnotation:annotation];
// NSLog(#"asdjhasdjsajdhaksdjghasdasdjahsdahskvdka");
self.currIdentifier = i;
i++;
}
[_mapView setShowsUserLocation:NO];
}
The other delegate functions
-(void) mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control {
for (NSObject<MKAnnotation> *annotation in _mapView.selectedAnnotations) {
NSLog(#"hellomaster");
NSLog(annotation.title);
if ([annotation isKindOfClass:[HCIAnnotationViewController class]]) {
NSLog(#"hellomaster");
}
}
The last one
-(MKAnnotationView*) mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
NSString *identifier = #"currIdentifier";
MKPinAnnotationView *annotationView =
(MKPinAnnotationView *)[_mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if (annotationView == nil) {
annotationView = [[MKPinAnnotationView alloc]
initWithAnnotation:annotation
reuseIdentifier:identifier];
} else {
annotationView.annotation = annotation;
}
annotationView.enabled = YES;
annotationView.canShowCallout = YES;
annotationView.tag = self.currIdentifier;
// Create a UIButton object to add on the
UIButton *rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
[rightButton setTitle:annotation.title forState:UIControlStateNormal];
[annotationView setRightCalloutAccessoryView:rightButton];
/*
UIButton *leftButton = [UIButton buttonWithType:UIButtonTypeInfoLight];
[leftButton setTitle:annotation.title forState:UIControlStateNormal];
[annotationView setLeftCalloutAccessoryView:leftButton];
*/
return annotationView;
}
But I see that the class equivalence fails. Can you tell me what I'm doing wrong?
I think what I want, in simple words, is, how can I send some data (NSDictionary*) along with a annotation such that I can retrieve it whenever I want?
Please dont tag this as repeated question or so. I have tried many questions, googling etc. but couldn't find a suitable solution for this.
Here You can also set NSMutableDictionary instand of NSString.
Create custom AnnotationView:
#import <MapKit/MapKit.h>
#interface AnnotationView : MKPlacemark
#property (nonatomic, readwrite, assign) CLLocationCoordinate2D coordinate;
#property (nonatomic, strong) NSString *title; //Here You cam set `NSMutableDictionary` instand of `NSString`
#property (nonatomic, strong) NSString *subtitle; //Here You cam set `NSMutableDictionary` instand of `NSString`
#end
And in .m file
#import "AnnotationView.h"
#implementation AnnotationView
- (id)initWithCoordinate:(CLLocationCoordinate2D)coordinate addressDictionary:(NSDictionary *)addressDictionary
{
if ((self = [super initWithCoordinate:coordinate addressDictionary:addressDictionary]))
{
self.coordinate = coordinate;
}
return self;
}
#end
// Use Annotation Add #import "AnnotationView.h" in your relevant .m file:
CLLocationCoordinate2D pCoordinate ;
pCoordinate.latitude = LatValue;
pCoordinate.longitude = LanValue;
// Create Obj Of AnnotationView class
AnnotationView *annotation = [[AnnotationView alloc] initWithCoordinate:pCoordinate addressDictionary:nil] ;
annotation.title = #"I m Here";
annotation.subtitle = #"This is Sub Tiitle";
[self.mapView addAnnotation:annotation];
I couldn't find exact way to implement custom data variables or the reason why my class equivalence fails. But I figured out a way to over come this kind of situation. This might be redundant in method, but still works like a charm.
So the idea is to use the tagging system in control button. I observed that every time I send a message to my map view to add a annotation it immediately calls my viewforannotation method. Since I'm iterating through an array, I maintained a index global pointer, with which I set tag of the control button. Later when the button is clicked, I retrieve the tag and use it to get the object I want.
Anyone else have any alternative or direct solution, please do post.

Resources