viewForAnnotation throws error when returning nil - objective-c

So I have implemented my viewForAnnotation as such
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
static NSString *identifier = #"id";
if ([annotation isKindOfClass:[infl8Node class]]) {
NSLog(#"Creating a pin");
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;
return annotationView;
}
return nil;
}
However I am still running into this error:
2012-11-18 22:12:35.608 Infl8[5960:c07] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[infl8Node coordinate]: unrecognized selector sent to instance 0x846f460'
Does anyone know what could be causing this error? I have traced it to the case where the annotation is of the class MKUserLocation.
EDIT:
Here is the infl8Node.h file:
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#import <MapKit/MapKit.h>
#interface infl8Node : NSObject <MKAnnotation>
#property (nonatomic, strong) NSString *accountName;
#property (nonatomic, strong) NSString *status;
#property (nonatomic, strong) CLLocation *location;
#property (nonatomic, strong) NSDictionary *address;
- (id) initWithDictionary:(NSDictionary *)dict;
- (MKMapItem *) mapItem;
#end

A class that implements the MKAnnotation protocol must implement the coordinate property.
The infl8Node class does not appear to do that which is why you are getting that unrecognized selector error.
At a minimum, the class will need to implement a coordinate method which returns the coordinate associated with its location.

Related

Xcode: Passing an object between two views?

I have eventually succeeded with getting the index for the annotation clicked on at callOutAccecoryControlTapped-method. :)
I have made a custom annotation class called Annotation (Annnotation *ann) which I fill with values from my db at parse.com.
But now I don't know how to pass the values on to my next view LAOpeningHoursViewController?
I can see all the values when I am debugging and *ann (se code) has for example: ann.cname="Olles cafe" etc. So that is good.
I pass the whole ann-object on to my next view. But I don't know how to get for example ann.cname on to my 'self.myLabel.text'. I show you the code:
If you have the kindness to answer me, please explain very well because I am a beginner.
Best is if you could have the trouble writing a code-snippet here… ;)
This is some code of the first view:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
MKPinAnnotationView *pinView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:#"pinView"];
if (!pinView) {
pinView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"pinView"];
pinView.pinColor = MKPinAnnotationColorRed;
pinView.animatesDrop = YES;
pinView.canShowCallout = YES;
UIButton *rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
pinView.rightCalloutAccessoryView = rightButton;
} else {
pinView.annotation = annotation;
}
return pinView;
}
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control {
Annotation *ann = [[mapView selectedAnnotations] objectAtIndex:0];
NSLog(#"ann.subtitle = %#", ann.subtitle);
NSLog(#"ann.cid = %#", ann.cid);
NSLog(#"ann.cstr = %#", ann.cstr);
NSLog(#"ann.cname = %#", ann.cname);
//this works fine, I get all the values
[self performSegueWithIdentifier: #"openingHours" sender:ann];
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"openingHours"]) {
LAOpeningHoursViewController *secondView = (LAOpeningHoursViewController*)segue.destinationViewController;
//PFObject *tempObject = [[myMapView selectedAnnotations] objectAtIndex:0];
Annotation *ann = (id)sender;
//Here is the problem: What to pass? I want to pass for example ann.cname that is declared in my custom annotation
//Both 'ann' and 'tempObject' has the right values for cname but I dont know how to pass it to the next view
secondView.myLabel.text = self.stringToPass;
//secondView.myLabel.text = ann.cname;
}
}
And here is the code from the second view called LAOpeningHoursViewController, that should receive the data:
- (void)viewDidLoad
{
[super viewDidLoad];
self.myLabel.text = ann.cname;
//recieving an error-code… of course
}
declared in LAOpeningHoursViewController.h as this
#property (strong, nonatomic) IBOutlet UILabel *myLabel;
#property (strong, nonatomic) NSString *textContent;
and finally, here is my Annotation.h-file where cname is the one that should go to 'myLabel'
#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;
#property(nonatomic, copy)UIButton *rightButton;
#property(nonatomic, retain)NSString *cid;
#property(nonatomic, retain)NSString *cname;
#property(nonatomic, retain)NSString *ctel;
#property(nonatomic, retain)NSString *cstr;
#property(nonatomic, retain)NSString *cmon;
#property(nonatomic, retain)NSString *ctue;
#property(nonatomic, retain)NSString *cwed;
#property(nonatomic, retain)NSString *passedId;
#property(nonatomic, retain)NSString *objectId;
#end
The sender parameter in [self performSegueWithIdentifier: #"openingHours" sender:ann]; should be set to self. This is intended to give the presented view controller reference to the presenting controller.
Add the property #property (nonatomic, strong) Annotation *selectedAnnotation to the presenting view controller. So that it can be set thus:
//this works fine, I get all the values
[self setSelectedAnnotation:ann];
[self performSegueWithIdentifier: #"openingHours" sender:ann];
Now you have the selected annotation as a property it can be used to initialize the LAOpeningHoursViewController:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"openingHours"]) {
LAOpeningHoursViewController *secondView = (LAOpeningHoursViewController*)segue.destinationViewController;
//PFObject *tempObject = [[myMapView selectedAnnotations] objectAtIndex:0];
Annotation *ann = (id)sender;
// Setup second view here:
secondView.myLabel.text = self.selectedAnnotation.cname;
// assign further properties here...
}
}
I hope this helps, good luck!

MKPinAnnotationView draggable not working. draggable = YES, callout has title and subtitle, and setCoordinate is implemented

When I try to drag the pin, it does not move. When I touch it, it darkens. However, it never lifts up, and it never moves around.
Here is the header file for my annotation class:
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#import <Parse/Parse.h>
#interface GeoPointAnnotation : NSObject <MKAnnotation>
- (id)initWithObject:(PFObject *)aObject;
#property (nonatomic, readwrite, assign) CLLocationCoordinate2D coordinate;
#property (nonatomic, readonly, copy) NSString *title;
#property (nonatomic, readonly, copy) NSString *subtitle;
- (void)setCoordinate:(CLLocationCoordinate2D)newCoordinate;
#end
Here's where I create the annotation in my view controller:
- (void)updateMap
{
[self.mapView removeAnnotations:self.mapView.annotations];
PFGeoPoint *geoPoint = [self.post location];
// center the map around the geopoint
[self.mapView setRegion:MKCoordinateRegionMake(
CLLocationCoordinate2DMake(geoPoint.latitude, geoPoint.longitude),
MKCoordinateSpanMake(0.01, 0.01)
)];
GeoPointAnnotation *annotation = [[GeoPointAnnotation alloc] initWithObject:self.post.object];
[self.mapView addAnnotation:annotation];
}
// from Geolocations by Parse
#pragma mark - MKMapViewDelegate
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
static NSString *GeoPointAnnotationIdentifier = #"RedPin";
MKPinAnnotationView *annotationView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:GeoPointAnnotationIdentifier];
if (!annotationView) {
annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:GeoPointAnnotationIdentifier];
annotationView.pinColor = MKPinAnnotationColorRed;
annotationView.canShowCallout = YES;
annotationView.draggable = YES;
annotationView.animatesDrop = YES;
}
return annotationView;
}
Any ideas?
Found the problem: I forgot to hook up the Map View's delegate in my Storyboard xib. h/t Nitin Gohel - thanks!

signal SIGABRT, when trying to get distance to annotations

I'm trying to calulate the distance between the userLocation and my annotations (from plist), But every time im run the mapView im getting signal SIGABRT error.
I'm doing the calculate in my mapViewController.m and want to show the calculated values in my tableViewController.
In my Annotations.h
#import <Foundation/Foundation.h>
#import <MapKit/MKAnnotation.h>
#interface Annotation : NSObject <MKAnnotation>
#property (nonatomic, assign) CLLocationCoordinate2D coordinate;
#property (nonatomic, assign) CLLocationDistance distance;
#property (nonatomic, copy) NSString * title;
#property (nonatomic, copy) NSString * subtitle;
#property (nonatomic, copy) NSString * sculptureIdKey;
#end
I'm synthesize distance = _distance; in my Annotations.m file.
In my mapViewController.m im doing the calculations
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
{
for (Annotation *annotation in self.mapView.annotations)
{
CLLocationCoordinate2D coord = [annotation coordinate];
CLLocation *annLocation = [[CLLocation alloc] initWithLatitude:coord.latitude longitude:coord.longitude];
annotation.distance = [annLocation distanceFromLocation:newLocation];
CLLocationDistance calculatedDistance = [annLocation distanceFromLocation:newLocation];
annotation.distance = calculatedDistance;
NSLog(#"Calculated Distance:%.2f m\n", calculatedDistance);
}
}
And in my tableViewController.h
#import "mainViewController.h"
#import "mapViewController.h"
#import "Annotation.h"
#interface ListViewController : UITableViewController <UITableViewDataSource, UITableViewDelegate, CLLocationManagerDelegate>
#property (readonly) CLLocationCoordinate2D selectedCoordinate;
#property (nonatomic, assign) CLLocationDistance distance;
#property (nonatomic, strong) CLLocationManager *locationManager;
#property (nonatomic, strong) CLLocation *userLocation;
#property (nonatomic, strong) NSArray *locationsArray;
#end
And tableViewController.m
#import "ListViewController.h"
#import "customCell.h"
#interface ListViewController ()
#end
#implementation ListViewController
#synthesize locationsArray = _locationsArray;
#synthesize locationManager = _locationManager;
#synthesize selectedCoordinate = _selectedCoordinate;
#synthesize distance = _distance;
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.locationsArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
customCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (!cell)
{
cell = [[customCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.customTitle.text = [[self.locationsArray objectAtIndex:indexPath.row] valueForKey:#"sculptureNameKey"];
cell.customSubTitle.text = [[self.locationsArray objectAtIndex:indexPath.row] valueForKey:#"sculptureAddressKey"];
cell.distanceLabel.text = [NSString stringWithFormat:#"Nice distance:%f m\n", _distance];
return cell;
}
My output in log is as follow
2013-02-15 11:05:50.769 TalkingSculpture[11639:c07] Calculated Distance:83971.36 m
2013-02-15 11:05:50.770 TalkingSculpture[11639:c07] Calculated Distance:83406.16 m
2013-02-15 11:05:50.771 TalkingSculpture[11639:c07] Calculated Distance:86002.30 m
2013-02-15 11:05:50.771 TalkingSculpture[11639:c07] -[MKUserLocation setDistance:]: unrecognized selector sent to instance 0xee4f080
2013-02-15 11:05:50.772 TalkingSculpture[11639:c07] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[MKUserLocation setDistance:]: unrecognized selector sent to instance 0xee4f080'
*** First throw call stack:
(0x1b44012 0x1460e7e 0x1bcf4bd 0x1b33bbc 0x1b3394e 0x3ef8 0x3637e 0x3593f 0x339c5 0x2f175 0x1b03920 0x1ac6d31 0x1aeac51 0x1ae9f44 0x1ae9e1b 0x1a9e7e3 0x1a9e668 0x3a4ffc 0x43ed 0x2535)
libc++abi.dylib: terminate called throwing an exception
In your for-loop where you set the annotation distance, you need to check that the annotation is not MKUserLocation since it is one of self.mapView.annotations. Otherwise, "annotation.distance" will apply to MKUserLocation, which doesn't have a distance property. Here's the update to your code:
for (Annotation *annotation in self.mapView.annotations)
{
if (![annotation isKindOfClass:[MKUserLocation class]]) //<-- Need this check
{
CLLocationCoordinate2D coord = [annotation coordinate];
CLLocation *annLocation = [[CLLocation alloc] initWithLatitude:coord.latitude longitude:coord.longitude];
annotation.distance = [annLocation distanceFromLocation:newLocation];
CLLocationDistance calculatedDistance = [annLocation distanceFromLocation:newLocation];
annotation.distance = calculatedDistance;
NSLog(#"Calculated Distance:%.2f m\n", calculatedDistance);
}
}

checking which map view annotation is tapped on mkmapview

I am having problem in knowing which annotation is tapped on MKMapView.
let me explain my problem, there is a simple view controller on which map view is loaded.
my annotation class "MapViewAnnotation.h" is as follows
#interface MapViewAnnotation : NSObject <MKAnnotation>
{
NSString *title;
CLLocationCoordinate2D coordinate;
NSString *sID;
NSString *zipCode;
}
#property (nonatomic, copy) NSString *title;
#property (nonatomic, assign) CLLocationCoordinate2D coordinate;
#property (nonatomic, retain) NSString *sID;
#property (nonatomic, retain) NSString *zipCode;
- (id)initWithTitle:(NSString *)titleOfPin andStoreId:(NSString *)storeIdForDetails andCoordinate:(CLLocationCoordinate2D)coordinateOfPin andZipCode:(NSString *)zip;
here is my "MapViewAnnotation.m" file.
#import "MapViewAnnotation.h"
#import <MapKit/MapKit.h>
#implementation MapViewAnnotation
#synthesize title, coordinate,storeId,zipCode;
- (id)initWithTitle:(NSString *)titleOfPin andStoreId:(NSString *)storeIdForDetails andCoordinate:(CLLocationCoordinate2D)coordinateOfPin andZipCode:(NSString *)zip
{
[super init];
title = titleOfPin;
coordinate = coordinateOfPin;
sID = storeIdForDetails;
zipCode = zip;
return self;
}
-(void)dealloc
{
[title release];
[super dealloc];
}
#end
and this is my viewcontroller.m file
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
//I want to access the sID property of that annotation here. PLEASE HELP ME HOW CAN I DO THAT
if (!storeDetailControllerObject) {
storeDetailControllerObject = [[StoreDetailController alloc]init];
}
// storeDetailControllerObject.storeId = [view.annotation storeId];
[self.navigationController pushViewController:storeDetailControllerObject animated:YES];
From the MKMapViewDelegate protocol reference:
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view;
If you handle the event in this method you may know which annotation was selected.
You have to set the MKMapView's delegate and to declare that you class implements the MKMapViewDelegate.
This should work:
MapViewAnnotation *mapViewAnnotation = (MapViewAnnotation*)view.annotation;
if( [mapViewAnnotation isKindOfClass:[MapViewAnnotation class]] ){
storeDetailControllerObject.storeId = mapViewAnnotation.storeId;
}

selectAnnotation does not show callout

I want to change the callout shown on the MKMapView. When I call
[self.mapView selectAnnotation:annotation animated:YES];
it pans the map to the annotation, but does not show its callout. How do I get it to show the callout?
Set the title, and optionally the subtitle property on the annotation object. MapKit will automatically show a callout.
#interface MyAnnotation : NSObject <MKAnnotation> {
NSString *title;
NSString *subtitle;
}
#property (nonatomic, copy) NSString *title;
#property (nonatomic, copy) NSString *subtitle;
#end
And the implementation,
#import "MyAnnotation.h"
#implementation MyAnnotation
#synthesize title, subtitle;
#end
Usage,
MyAnnotation *foo = [[MyAnnotation alloc] init];
foo.title = #"I am Foo";
foo.subtitle = "I am jus' a subtitle";
You should implement the MKAnnotation Delegate method
-(MKAnnotationView *)mapView:(MKMapView *)_mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
static NSString *AnnotationViewIdentifier = #"AnnotationViewIdentifier";
MKPinAnnotationView *annotationView = (MKPinAnnotationView *)[_mapView dequeueReusableAnnotationViewWithIdentifier:AnnotationViewIdentifier];
if (annotationView == nil)
{
annotationView = [[[MKPinAnnotationView alloc]initWithAnnotation:annotation reuseIdentifier:AnnotationViewIdentifier]autorelease];
}
annotationView.canShowCallout = YES;
return annotationView;
}