Custom MKAnnotationView Showing Wrong Image - ios

I have an MKMapView where I want to display annotations of contacts' faces. I looked at Apple's sample app MapCallouts and have so far written this:
Annotation.h
#interface Annotation : NSObject <MKAnnotation>
#property (nonatomic, strong) UIImage *image;
#property (nonatomic, readwrite) CLLocationCoordinate2D coordinate;
+ (MKAnnotationView *)createViewAnnotationForMapView:(MKMapView *)mapView annotation:(id <MKAnnotation>)annotation;
#end
Annotation.m
#import "CustomAnnotationView.h"
#implementation Annotation
+ (MKAnnotationView *)createViewAnnotationForMapView:(MKMapView *)mapView annotation:(id <MKAnnotation>)annotation
{
MKAnnotationView *returnedAnnotationView =
(CustomAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:#"Annotation"];
if (returnedAnnotationView == nil)
{
returnedAnnotationView =
[[CustomAnnotationView alloc] initWithAnnotation:annotation
reuseIdentifier:#"Annotation"];
}
return returnedAnnotationView;
}
#end
CustomAnnotationView.h
#interface CustomAnnotationView : MKAnnotationView
CustomAnnotationView.m
- (id)initWithAnnotation:(id <MKAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithAnnotation:annotation reuseIdentifier:#"Annotation"];
if (self != nil)
{
Annotation *mapItem = (Annotation *)self.annotation;
// Create imageView and labels, etc.
}
return self;
}
Then, to drop my annotation, I have this method which loops through an array of contacts, gets their address, finds the CLLOcationCoordinate2D, and creates an Annotation.
-(void)sortContacts{
for (APContact *contact in self.peopleArray){
[operationQueue addOperationWithBlock:^{
NSString *addressString = // get address from contact;
contact.addressString = addressString;
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder geocodeAddressString:addressString
completionHandler:^(NSArray* placemarks, NSError* error){
if (placemarks && placemarks.count > 0 && !error) {
CLPlacemark *topResult = [placemarks objectAtIndex:0];
CLLocation *location = topResult.location;
CLLocationCoordinate2D coordinate = location.coordinate;
Annotation *annotation = [[Annotation alloc] init];
annotation.image = [UIImage imageNamed:#"Annotation"];
if (contact.thumbnail.size.width > 0){
annotation.image = contact.thumbnail;
}
else{
annotation.image = [UIImage imageNamed:#"Annotation"];
}
annotation.coordinate = coordinate;
[self.mapView addAnnotation:annotation];
}
else if (error){
NSLog(#"ERROR");
}
}
];
}];
}
}
-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation{
CustomAnnotationView *returnedAnnotationView = nil;
if ([annotation isKindOfClass:[Annotation class]])
{
returnedAnnotationView = (CustomAnnotationView *)[Annotation createViewAnnotationForMapView:self.mapView annotation:annotation];
}
return returnedAnnotationView;
}
However, when I scroll across self.mapView, the incorrect contact images are showing for the wrong annotations. I'm not sure how to fix this. Any help would be much appreciated. Thanks

I added a property to my custom annotation view and simply changed the image everytime I dequeued or created a new annotation.
#property (nonatomic, strong) UIImageView *annotationImage;

Related

how to create a custom class that conforms to <MKAnnotation> protocol

I have a map that is displaying all restaurant objects. I'm trying to pass the restaurant object to the map annotation so that i can display a detail view with all the restaurant info. After researching, I'm trying to create a class that conforms to protocols, however I haven't been able to place the annotation in the map. Here is my code:
RestaurantAnnotationClass.h (custom class):
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#import "Restaurant.h"
static NSString *restaurantAnnotationIdentifier = #"restaurantAnnotationIdentifier";
#interface RestaurantAnnotation : NSObject <MKAnnotation>
#property (copy, nonatomic) NSString *title;
#property (copy, nonatomic) NSString *subtitle;
#property (nonatomic) CLLocationCoordinate2D coordinate;
#property (nonatomic, strong) Restaurant *restaurant;
- (id)initWithTitle:(NSString *)newTitle subtitle:(NSString *)newSubtitle restaurant:(Restaurant *)newRestaurant location:(CLLocationCoordinate2D)location;
- (MKAnnotationView *)annotationView;
#end
RestaurantAnnotationClass.m
#import "RestaurantAnnotation.h"
#implementation RestaurantAnnotation
- (id)initWithTitle:(NSString *)newTitle subtitle:(NSString *)newSubtitle restaurant:(Restaurant *)newRestaurant location:(CLLocationCoordinate2D)location {
self = [super init];
if (self) {
self.title = newTitle;
self.coordinate = location;
self.subtitle = newSubtitle;
self.restaurant = newRestaurant;
}
return self;
}
- (MKAnnotationView *)annotationView {
MKAnnotationView *annotationView = [[MKAnnotationView alloc] initWithAnnotation:self reuseIdentifier:restaurantAnnotationIdentifier];
annotationView.enabled = YES;
annotationView.canShowCallout = YES;
annotationView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
return annotationView;
}
#end
My view Controller with the map
- (NSArray *)convertRestaurantToMKAnnotationsFromArray:(NSArray *)restaurantsArray {
NSMutableArray *mkPointAnnotations = [NSMutableArray new];
double mileToMeters = 1609.344;
CLLocation *userLocation = [[CLLocation alloc] initWithLatitude:self.userCurrentLocation.coordinate.latitude longitude:self.userCurrentLocation.coordinate.longitude];
for (Restaurant *restaurant in restaurantsArray) {
CLLocation *restaurantLocation = [[CLLocation alloc] initWithLatitude:restaurant.geoLocation.latitude longitude:restaurant.geoLocation.longitude];
RestaurantAnnotation *newRestaurantAnnotation = [[RestaurantAnnotation alloc] initWithTitle:restaurant.name subtitle:[NSString stringWithFormat:#"%.02f",[userLocation distanceFromLocation:restaurantLocation] / mileToMeters] restaurant:restaurant location:CLLocationCoordinate2DMake(restaurant.geoLocation.latitude, restaurant.geoLocation.longitude)];
[mkPointAnnotations addObject: newRestaurantAnnotation];
}
return mkPointAnnotations;
}
here im implementing the vuewforannotation method
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil;
if ([annotation isKindOfClass:[RestaurantAnnotation class]]) {
RestaurantAnnotation *restaurant = (RestaurantAnnotation *)annotation;
MKAnnotationView *restaurantAnnotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:restaurantAnnotationIdentifier];
if (restaurantAnnotationView == nil)
restaurantAnnotationView = restaurant.annotationView;
else
restaurantAnnotationView.annotation = annotation;
return restaurantAnnotationView;
} else
return nil;
}
thank you in advance
I was making a mistake when applying the method viewForAnnotation, this is my code now
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
if ([annotation isKindOfClass:[MKUserLocation class]]) {
return nil;
}
if ([annotation isKindOfClass:[RestaurantAnnotation class]]) {
RestaurantAnnotation *restauntAnnotation = (RestaurantAnnotation *)annotation;
MKAnnotationView *restaurantAnnotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:restaurantAnnotationIdentifier];
if (restaurantAnnotationView == nil) {
restaurantAnnotationView = restauntAnnotation.annotationView;
} else {
restaurantAnnotationView.annotation = annotation;
return restaurantAnnotationView;
}
}
return nil;

Pins have disappeared when using MKAnnotationView delegate

My MKAnnotationView delegate method is been called as I can see my NSLog output. However the pins are not appearing on the map. Is there something I'm missing here?
MapViewController.h
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#interface MapViewController : UIViewController <MKMapViewDelegate>
#property (weak, nonatomic) IBOutlet MKMapView *nearbyMapView;
#end
MapViewController.m
#import "MapViewController.h"
#import "AppDelegate.h"
#interface MapViewController ()
#end
#implementation MapViewController
AppDelegate *appDelegate;
- (void)viewDidLoad
{
[super viewDidLoad];
appDelegate=[[UIApplication sharedApplication] delegate];
CLLocationCoordinate2D center = CLLocationCoordinate2DMake(54.995184, -1.566699);
MKCoordinateSpan span = MKCoordinateSpanMake(0.5, 0.5);
MKCoordinateRegion regionToDisplay = MKCoordinateRegionMake(center, span);
[self.nearbyMapView setRegion: regionToDisplay];
for (int i = 0; i < [[appDelegate offersFeeds] count]; i++) {
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
NSString *plotAddress = [[[appDelegate offersFeeds] objectAtIndex:i] valueForKey:#"addressline"];
NSString *plotTitle = [[[appDelegate offersFeeds] objectAtIndex:i] valueForKey:#"title"];
[geocoder geocodeAddressString:plotAddress completionHandler:^(NSArray *placemarks, NSError *error) {
if (placemarks && placemarks.count > 0)
{
CLPlacemark *topResult = [placemarks objectAtIndex:0];
MKPlacemark *placemark = [[MKPlacemark alloc]initWithPlacemark:topResult];
MKPointAnnotation *pa = [[MKPointAnnotation alloc] init];
pa.coordinate = placemark.location.coordinate;
pa.title = plotTitle;
[self.nearbyMapView addAnnotation:pa];
}
}];
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
MKAnnotationView *pinView = nil;
static NSString *defaultPinID = #"identifier";
pinView = (MKAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:defaultPinID];
if ( pinView == nil ) {
NSLog(#"Inside IF");
pinView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:defaultPinID];
pinView.enabled = YES;
pinView.canShowCallout = YES;
UIButton *btn = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
//Accessoryview for the annotation view in ios.
pinView.rightCalloutAccessoryView = btn;
}
else {
pinView.annotation = annotation;
}
pinView.annotation = annotation;
return pinView;
}
#end
In viewForAnnotation, the code is creating an MKAnnotationView but not setting an image for it. There is no default image on an MKAnnotationView so the annotations are invisible.
When you don't implement the delegate at all, the map view creates MKPinAnnotationViews for you with a red pin color. MKPinAnnotationView is a convenient subclass of MKAnnotationView which supplies a pin image (in one of three colors).
When you implement the delegate, it's up to you to create the right view and set the properties as needed.
Either create an MKPinAnnotationView instead (which provides a default pin image) or set the image property on the plain MKAnnotationView.
To use MKPinAnnotationView:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
MKPinAnnotationView *pinView = nil;
static NSString *defaultPinID = #"identifier";
pinView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:defaultPinID];
if ( pinView == nil )
{
NSLog(#"Inside IF");
pinView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:defaultPinID];
pinView.pinColor = MKPinAnnotationColorRed; //or Green or Purple
pinView.enabled = YES;
pinView.canShowCallout = YES;
UIButton *btn = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
//Accessoryview for the annotation view in ios.
pinView.rightCalloutAccessoryView = btn;
}
else
{
pinView.annotation = annotation;
}
return pinView;
}
or to use MKAnnotationView and your own image:
//same code as the current but add this line
//after the initWithAnnotation:
pinView.image = [UIImage imageNamed:#"SomeImage.png"];

MKAnnotationView - Set two different Pin colours

I'm using Parse.com as a backend and i want to show Geopoints on my map.
Every Geopoint is also connected with a database boolean field true or false.
How can I show a green pins colour for the "true" gepoint and red pins for the "false" Geopoint?
Here is code for the MapViewController.m
#property (weak, nonatomic) IBOutlet MKMapView *mapView;
I have a function to perform the query against the parse.com database to return me all location data. It is called within the viewDidLoad Method.
- (void)viewDidLoad
{
[super viewDidLoad];
[self getAllStations];
}
Then I set the annotationView like this:
#pragma mark - MapViewDelegate
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)geoPointAnnotation {
static NSString *MapViewAnnotationIdentifier = #"Places";
MKPinAnnotationView *annotationView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:MapViewAnnotationIdentifier];
if (geoPointAnnotation == mapView.userLocation) {
return nil;
} else {
annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:geoPointAnnotation reuseIdentifier:MapViewAnnotationIdentifier];
annotationView.pinColor = MKPinAnnotationColorGreen;
annotationView.canShowCallout = YES;
annotationView.draggable = YES;
annotationView.animatesDrop = YES;
}
return annotationView;
}
Here is the Code for the MapViewAnnotation.m (Object):
#import "MapViewAnnotation.h"
#import "Parse/Parse.h"
#interface MapViewAnnotation ()
#property (nonatomic, strong) PFObject *object;
#end
#implementation MapViewAnnotation
#pragma mark - Initialization
- (id)initWithObject:(PFObject *)aObject {
self = [super init];
if (self) {
_object = aObject;
PFGeoPoint *geoPoint = self.object[#"location"];
[self setGeoPoint:geoPoint]; }
return self;
}
- (void)setGeoPoint:(PFGeoPoint *)geoPoint {
_coordinate = CLLocationCoordinate2DMake(geoPoint.latitude, geoPoint.longitude);
NSString *streetName = self.object[#"adress"];
_title = [NSString stringWithFormat:#"%#", [_object objectForKey:#"name"]];
[PFGeoPoint geoPointForCurrentLocationInBackground:^(PFGeoPoint *currentLocationGeoPoint, NSError *error) { //Get current Location
if (!error) {
PFGeoPoint *distanceGeoPoint = [_object objectForKey:#"location"];
double distanceDouble = [currentLocationGeoPoint distanceInKilometersTo:distanceGeoPoint];
//NSLog(#"Distance: %.1f",distanceDouble);
_subtitle = [NSString stringWithFormat:#"%# - Distance: %.1f km", streetName, distanceDouble];
}
}];
}
#end
Can anyone give me a hint how I can show green and red pins based on a boolean?
Thanks in advance!
You can customize the pin annotation view,
- (MKAnnotationView *)mapView:(MKMapView *)theMapView viewForAnnotation:(id <MKAnnotation>)annotation
{
if ([annotation isKindOfClass:[MKUserLocation class]])
{
return nil;
}
MKAnnotationView *flagAnnotationView =
[self.mapView dequeueReusableAnnotationViewWithIdentifier:SFAnnotationIdentifier];
if (flagAnnotationView == nil)
{
MKPinAnnotationView *customPinView = [[MKPinAnnotationView alloc]
initWithAnnotation:annotation reuseIdentifier:SFAnnotationIdentifier];
if(annotation.coordinate.latitude==42.0000 && annotation.coordinate.longitude==-87.65000)
//you have to keep the latitude and longitude of the pins to which you can set the colour of the pin according to that latlong
customPinView.pinColor = MKPinAnnotationColorGreen;
else
customPinView.pinColor = MKPinAnnotationColorRed;
customPinView.animatesDrop = YES;
customPinView.canShowCallout = YES;
return customPinView;
}
else
{
flagAnnotationView.annotation = annotation;
}
return flagAnnotationView;
return nil;
}
in your annotation.h file declare a nsstring as below
#property (strong , nonatomic)NSString *pinColour;
and in the implemention file do the below to check for the colour
-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id)annotation{
//other code above
if ([[annotation pinColour] isEqualToString:#"Red"]) {
annotationView.pinColor = MKPinAnnotationColorRed;
}else{
annotationView.pinColor = MKPinAnnotationColorGreen;
}
}
and inside the for loop of parse, tag the relevant with to pass wither colour
annotation.pinColour = #"Red";
Can anyone give me a hint how I can show green and red pins based on a boolean?
BOOL colorRed = YES;
if (colorRed) annotationView.pinColor = MKPinAnnotationColorRed;
else annotationView.pinColor = MKPinAnnotationColorGreen;
Is that what you mean?

Can't get viewForAnnotation to fire after initial user location firing

I have this code:
// MapViewController.h
#import <Foundation/Foundation.h>
#import "MyCLController.h"
#import <MapKit/MapKit.h>
#class ShowsContainerController;
#interface MapViewController : UIViewController <MyCLControllerDelegate,MKMapViewDelegate> {
MyCLController *locationController;
MKMapView *mapView;
}
- (void)locationUpdate:(CLLocation *)location;
- (void)locationError:(NSError *)error;
#property (nonatomic, retain) IBOutlet MKMapView *mapView;
#property (assign) BOOL updateMap;
#property (strong) ShowsContainerController *parent;
#end
// MapViewController.m
#import "MapViewController.h"
#import "ShowsContainerController.h"
#define METERS_PER_MILE 1609.344
#implementation MapViewController
#synthesize parent, mapView;
- (void)viewDidLoad
{
[super viewDidLoad];
self.updateMap = YES;
locationController = [[MyCLController alloc] init];
locationController.delegate = self;
[locationController.locationManager startUpdatingLocation];
mapView.delegate = self;
}
- (void)locationUpdate:(CLLocation *)location {
if( self.updateMap ){
MKCoordinateRegion viewRegion = MKCoordinateRegionMakeWithDistance(location.coordinate, 0.5*METERS_PER_MILE, 0.5*METERS_PER_MILE);
[mapView setRegion:viewRegion animated:YES];
self.parent = (ShowsContainerController *)self.parentViewController;
[self.parent setLocation:location];
self.updateMap = NO;
}
}
- (void)locationError:(NSError *)error {
NSLog([error localizedDescription]);
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
self.parent.mapReady = YES;
if( self.parent.locationSet ){
[self.parent callApi];
}
}
#pragma mark MKMapViewDelegate
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
NSLog(#"delegating user location");
// if it's the user location, just return nil.
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil;
NSLog(#"delegating pins");
static NSString* ShowAnnotationIdentifier = #"showAnnotationIdentifier";
MKPinAnnotationView* pinView = (MKPinAnnotationView *)
[mapView dequeueReusableAnnotationViewWithIdentifier:ShowAnnotationIdentifier];
if (!pinView)
{
// if an existing pin view was not available, create one
MKPinAnnotationView* customPinView = [[MKPinAnnotationView alloc]
initWithAnnotation:annotation reuseIdentifier:ShowAnnotationIdentifier];
customPinView.enabled = YES;
customPinView.pinColor = MKPinAnnotationColorRed;
customPinView.animatesDrop = YES;
customPinView.canShowCallout = YES;
return customPinView;
}
else
{
pinView.annotation = annotation;
}
return pinView;
}
#pragma mark -
#end
And here's the code adding my pins; it isn't fired until an api call is made, which is after viewDidAppear is fired:
- (void)placeAnnotations:(NSArray *)shows
{
NSEnumerator *e = [shows objectEnumerator];
id show;
NSMutableArray *annotations = [[NSMutableArray alloc] init];
while (show = [e nextObject]) {
double lat = [[[[[show objectForKey:#"venue"] objectForKey:#"location"] objectForKey:#"geo:point"] objectForKey:#"geo:lat"] doubleValue];
double lng = [[[[[show objectForKey:#"venue"] objectForKey:#"location"] objectForKey:#"geo:point"] objectForKey:#"geo:long"] doubleValue];
ShowAnnotation *annotation = [[ShowAnnotation alloc]
initWithCoordinate:CLLocationCoordinate2DMake(lat, lng)
withMap:self.mapController.mapView
withTitle:[show objectForKey:#"title"]
withSubtitle:[[show objectForKey:#"venue"] objectForKey:#"name" ]];
[annotations addObject:annotation];
}
NSLog(#"adding annotations");
[self.mapController.mapView addAnnotations:annotations];
}
The log shows "delegating user location", but never "delegating pins". It doesn't even fire viewForAnnotation more than the one initial time for the user's location.
The delegation seems to be working, otherwise I wouldn't be able to capture this selector, but why is it firing for only the user location and not firing for subsequent annotation additions?

Annotation callout title does not change

I have searched but i didn't found any solution to my problem. I want to change already added pin. And here is my code. I'm getting "Code Here" in console.
I have in .h
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)annotationView;
myViewController.m
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)annotationView
{
userPins *myAnnot = (userPins *)annotationView.annotation;
[myAnnot setTitle:#"o21"];
NSLog(#"Code here!");
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
static NSString *identifier = #"userPins";
if ([annotation isKindOfClass:[userPins class]]) {
MKPinAnnotationView *annotationView = (MKPinAnnotationView *) [_mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if (annotationView == nil) {
annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
} else {
annotationView.annotation = annotation;
}
annotationView.image = [UIImage imageNamed:#"mapMarker-blue.png"];
annotationView.calloutOffset = CGPointMake(0,0);
UIButton *detailButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
annotationView.rightCalloutAccessoryView = detailButton;
annotationView.enabled = YES;
annotationView.canShowCallout = YES;
return annotationView;
}
return nil;
}
In userPins.m i have. But i didn't do this with setTitle too.
- (void)setTitle:(NSString *)title {
if (_subtitle != subtitle) {
[_subtitle release];
_subtitle = [subtitle retain];
}
}
- (NSString *)title{
return #"okan";
}
Edit: #AnnaKarenina
I have tried all. But setTitle is not running. I'm going crazy! This is all of my code. And I'm not getting errors.
userpins.m
#import "userPins.h"
#implementation userPins
#synthesize fbid = _fbid;
#synthesize coordinate = _coordinate;
#synthesize title = _title;
#synthesize subtitle = _subtitle;
- (id)initWithfbid:(NSInteger*)fbid
coordinate:(CLLocationCoordinate2D)coordinate {
if ((self = [super init])) {
_fbid = fbid;
_coordinate = coordinate;
}
return self;
}
- (NSString *)subtitle{
return #"Anything";
}
- (NSString *)title{
return #"Something";
}
- (void)dealloc
{
[_title release];
[_subtitle release];
[super dealloc];
}
#end
userPins.h
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface userPins : NSObject <MKAnnotation> {
NSInteger *_fbid;
CLLocationCoordinate2D _coordinate;
NSString *_title;
NSString *_subtitle;
}
#property (nonatomic, readonly) NSInteger *fbid;
#property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
#property (nonatomic, copy) NSString *title;
#property (nonatomic, copy) NSString *subtitle;
- (id)initWithfbid:(NSInteger*)fbid coordinate:(CLLocationCoordinate2D)coordinate;
#end
viewController.m
#import "lociseViewController.h"
#import "ASIHTTPRequest.h"
#import "SBJSON.h"
#import "userPins.h"
#implementation lociseViewController
#synthesize mapView = _mapView;
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
// Add new method above refreshTapped
- (void)plotPositions:(NSString *)responseString {
for (id<MKAnnotation> annotation in _mapView.annotations) {
[_mapView removeAnnotation:annotation];
}
NSDictionary * root = [responseString JSONValue];
NSArray *data = [root objectForKey:#"data"];
for (NSArray * row in data) {
NSInteger * fbid = (NSInteger *)[row objectAtIndex:0];
NSNumber * latitude = [row objectAtIndex:1];
NSNumber * longitude = [row objectAtIndex:2];
CLLocationCoordinate2D coordinate;
coordinate.latitude = latitude.doubleValue;
coordinate.longitude = longitude.doubleValue;
userPins *annotation = [[[userPins alloc] initWithfbid:fbid coordinate:coordinate] autorelease];
[_mapView addAnnotation:annotation];
}
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
// 1
MKCoordinateRegion mapRegion = [_mapView region];
CLLocationCoordinate2D centerLocation = mapRegion.center;
// 2
NSString *jsonFile = [[NSBundle mainBundle] pathForResource:#"command" ofType:#"json"];
NSString *formatString = [NSString stringWithContentsOfFile:jsonFile encoding:NSUTF8StringEncoding error:nil];
NSString *json = [NSString stringWithFormat:formatString, centerLocation.latitude, centerLocation.longitude, 0.5*METERS_PER_MILE];
NSURL *url = [NSURL URLWithString:#"http://localhost/users.json"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
request.requestMethod = #"POST";
//request.defaultResponseEncoding = NSISOLatin1StringEncoding;
//[request addRequestHeader:#"Content-Encoding" value:#"windows-1255"];
[request addRequestHeader:#"Content-Type" value:#"application/json"];
[request appendPostData:[json dataUsingEncoding:NSUTF8StringEncoding]];
[request setDefaultResponseEncoding:NSUTF8StringEncoding];
// 5
[request setDelegate:self];
[request setCompletionBlock:^{
NSString *responseString = [request responseString];
NSLog(#"Response: %#", responseString);
[self plotPositions:responseString];
}];
[request setFailedBlock:^{
NSError *error = [request error];
NSLog(#"Error: %#", error.localizedDescription);
}];
// 6
[request startAsynchronous];
//CLLocationManager *locationManager;
//locationManager.delegate = self;
[super viewDidLoad];
}
- (void)viewWillAppear:(BOOL)animated {
// 1
CLLocationCoordinate2D zoomLocation;
zoomLocation.latitude = 35.333070;
zoomLocation.longitude = 33.302875;
// 2
MKCoordinateRegion viewRegion = MKCoordinateRegionMakeWithDistance(zoomLocation, 50*METERS_PER_MILE, 3*METERS_PER_MILE);
// 3
MKCoordinateRegion adjustedRegion = [_mapView regionThatFits:viewRegion];
// 4
[_mapView setRegion:adjustedRegion animated:YES];
}
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)annotationView
{
userPins *myAnnot = (userPins *)annotationView.annotation;
[myAnnot setTitle:#"o21"];
NSLog(#"Title: %#", annotationView.annotation.subtitle);
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
static NSString *identifier = #"userPins";
if ([annotation isKindOfClass:[userPins class]]) {
MKPinAnnotationView *annotationView = (MKPinAnnotationView *) [_mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if (annotationView == nil) {
annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
} else {
annotationView.annotation = annotation;
}
annotationView.image = [UIImage imageNamed:#"mapMarker-blue.png"];
annotationView.calloutOffset = CGPointMake(0,0);
UIButton *detailButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
annotationView.rightCalloutAccessoryView = detailButton;
annotationView.enabled = YES;
annotationView.canShowCallout = YES;
return annotationView;
}
return nil;
}
- (void)viewDidUnload
{
[self setMapView:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (void)dealloc {
[_mapView release];
[super dealloc];
}
#end
viewController.h
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#define METERS_PER_MILE 1609.344
#interface lociseViewController : UIViewController <MKMapViewDelegate> {
MKMapView *_mapView;
}
#property (nonatomic, retain) IBOutlet MKMapView *mapView;
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)annotationView;
#end
The setTitle: and title methods in userPins.m don't look right.
The setTitle: method is looking at and setting subtitle instead of title. The title method returns a constant string so even if setTitle: set the title ivar, the annotation will return the same constant string.
You could fix those methods or just declare title and subtitle as copy properties and #synthesize them in userPins.m (and release in dealloc). Then you don't need those methods at all (they will be implemented automatically).
By the way, when you add the annotation, be sure to set the title to something (even a single space) otherwise the callout will not display and didSelectAnnotationView will not be called.
A separate problem is a memory leak in viewForAnnotation. You should add an autorelease where you alloc+init annotationView.
Regarding The Updated Code:
In userPins.m, it still returns constant strings for subtitle and title thus hiding the changed ivars. Change it to:
- (id)initWithfbid:(NSInteger*)fbid
coordinate:(CLLocationCoordinate2D)coordinate {
if ((self = [super init])) {
_fbid = fbid;
_coordinate = coordinate;
//init the ivars here...
_title = #"Something";
_subtitle = #"Anything";
}
return self;
}
/* comment these methods out
- (NSString *)subtitle{
return #"Anything";
}
- (NSString *)title{
return #"Something";
}
*/
A new, unrelated problem is that fbid should probably be NSNumber * instead of NSInteger *. NSInteger is not an object and an NSNumber is what the JSON parser is most likely giving you.

Resources