Control may reach end of non-void function - iphone

I dunno why xcode is showing this message, have a look at my code:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation{
if (annotation==self.mapView.userLocation) {
return nil;
NSString *pinID = #"Salvar";
MKPinAnnotationView *view = (MKPinAnnotationView*)
[self.mapView dequeueReusableAnnotationViewWithIdentifier:pinID];
if (view==nil) {
view = [[MKPinAnnotationView alloc]initWithAnnotation:annotation reuseIdentifier:pinID];
view.canShowCallout = YES;
view.animatesDrop = YES;
}
return view;
}
}

No return value is provided in the case where annotation!=self.mapView.userLocation.
To repair, add an else block to the first conditional, or return something unconditionally before the final close curly.
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation{
if (annotation==self.mapView.userLocation) {
// the code that you have already here
} else {
return nil; // or whatever you would return if annotation!= userLocation
}
// or, instead of the else above...
return nil;
}

Related

The rightCalloutAccessory button is not shown

I try to manage annotations, and to display an info button on the right of the view when a PIN get selected, my relevant code is this:
- (MKAnnotationView *)mapView:(MKMapView *)map viewForAnnotation:(id <MKAnnotation>)annotation {
MKPinAnnotationView *newAnnotation = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"greenPin"];
if ([annotation isKindOfClass:[ManageAnnotations class]]) {
static NSString* identifier = #"ManageAnnotations";
MKPinAnnotationView *newAnnotation = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
if (newAnnotation==nil) {
newAnnotation=[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
}else {
newAnnotation.annotation=annotation;
}
newAnnotation.pinColor = MKPinAnnotationColorGreen;
newAnnotation.animatesDrop = YES;
newAnnotation.canShowCallout = YES;
newAnnotation.rightCalloutAccessoryView=[UIButton buttonWithType:UIButtonTypeInfoLight];
return newAnnotation;
}else {
newAnnotation.pinColor = MKPinAnnotationColorGreen;
newAnnotation.animatesDrop = YES;
newAnnotation.canShowCallout = YES;
return newAnnotation;
}
ManageAnnotations.m :
#implementation ManageAnnotations
#synthesize pinColor;
#synthesize storeName=_storeName;
#synthesize storeAdress=_storeAdress;
#synthesize coordinate=_coordinate;
-(id)initWithTitle:(NSString*)storeName adress:(NSString*)storeAdress coordinate:(CLLocationCoordinate2D)coordinate{
if((self=[super init])){
_storeName=[storeName copy];
_storeAdress=[storeAdress copy];
_coordinate=coordinate;
}
return self;
}
-(NSString*)title{
return _storeName;
}
-(NSString*)subtitle{
return _storeAdress;
}
ManageAnnotations.h
#interface ManageAnnotations : NSObject<MKAnnotation>{
NSString *_storeName;
NSString *_storeAdress;
CLLocationCoordinate2D _coordinate;
}
//
#property(nonatomic,assign)MKPinAnnotationColor pinColor;
#property(nonatomic, readonly, copy)NSString *storeName;
#property(nonatomic, readonly, copy)NSString *storeAdress;
#property(nonatomic,readonly)CLLocationCoordinate2D coordinate;
//
-(id)initWithTitle:(NSString*)storeName adress:(NSString*)storeAdress coordinate:(CLLocationCoordinate2D)coordinate;
//
The PINS are shown correctly on the Map, but without the info button on the right of the view. Am i missing something?
Are you actually going into the condition? Set a breakpoint to check.
Why create a MKPinAnnotationView at the beginning, before you know the type of annotation?
You should dequeue your annotationView instead of alloc/initWithAnnotation:reuseIdentifier
When you reuse your annotations, you should put everything that doesn't change (color, animation, etc) after the alloc init, and not reset them all the time. Otherwise you lose the interest of reusing.
Other than that your code seems fine, and comparing it to mine, I don't see anything obvious. Remark 1 is the most probable. I would set a breakpoint to see if I really go there, see if I can show a leftCalloutAccessoryView instead, use a different pinColor.

Drag an annotation pin on a mapview

The annotation pin is draggable but I couldn't drop it to the destination location. The problem is how could I get the destination location's coordinate where i drop the annotation pin? Any method exist?
Edit 1:
The center of the annotation pin seems never changed where ever I drop the pin.
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)annotationView didChangeDragState:(MKAnnotationViewDragState)newState
fromOldState:(MKAnnotationViewDragState)oldState
{
if (newState == MKAnnotationViewDragStateEnding)
{
NSLog(#"x is %f", annotationView.center.x);
NSLog(#"y is %f", annotationView.center.y);
CGPoint dropPoint = CGPointMake(annotationView.center.x, annotationView.center.y);
CLLocationCoordinate2D newCoordinate = [self.mapView convertPoint:dropPoint toCoordinateFromView:annotationView.superview];
[annotationView.annotation setCoordinate:newCoordinate];
}
}
Edit 2:
Annotation.h
#property (nonatomic,readwrite,assign) CLLocationCoordinate2D coordinate;
Annotation.m
- (void)setCoordinate:(CLLocationCoordinate2D)newCoordinate {
coordinate = newCoordinate;
}
Edit 3:
where ever I drag the pin, the NSLog out put of the droppedAt is always the same.
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)annotationView didChangeDragState:(MKAnnotationViewDragState)newState
fromOldState:(MKAnnotationViewDragState)oldState
{
if (newState == MKAnnotationViewDragStateEnding)
{
CLLocationCoordinate2D droppedAt = annotationView.annotation.coordinate;
NSLog(#"Pin dropped at %f,%f", droppedAt.latitude, droppedAt.longitude);
}
}
Edit 4:
Annotaion.m
#dynamic startAddr;
- (CLLocationCoordinate2D)coordinate
{
coordinate.latitude = [self.startAddr.latitude doubleValue];
coordinate.longitude = [self.startAddr.longitude doubleValue];
return coordinate;
}
- (void)setCoordinate:(CLLocationCoordinate2D)newCoordinate {
//I need to update the self.startAddr.latitude and longitude here. Problem solved.
self.startAddr.latitude = [NSNumber numberWithDouble:newCoordinate.latitude];
self.startAddr.longitude = [NSNumber numberWithDouble:newCoordinate.longitude];
coordinate = newCoordinate;
}
First you have to create a custom annotation like this:
MyAnnotation.h
#import <MapKit/MapKit.h>
#interface MyAnnotation : NSObject <MKAnnotation>{
CLLocationCoordinate2D coordinate;
}
- (id)initWithCoordinate:(CLLocationCoordinate2D)coord;
- (void)setCoordinate:(CLLocationCoordinate2D)newCoordinate;
#end
MyAnnotation.m
#import "MyAnnotation.h"
#implementation MyAnnotation
#synthesize coordinate;
- (NSString *)subtitle{
return nil;
}
- (NSString *)title{
return nil;
}
-(id)initWithCoordinate:(CLLocationCoordinate2D)coord {
coordinate=coord;
return self;
}
-(CLLocationCoordinate2D)coord
{
return coordinate;
}
- (void)setCoordinate:(CLLocationCoordinate2D)newCoordinate {
coordinate = newCoordinate;
}
#end
After that, you just have to use your annotation like this:
// Assuming you have imported MyAnnotation.h and you have a self.map property pointing to a MKMapView
MyAnnotation *myPin = [[MyAnnotation alloc] initWithCoordinate:self.map.centerCoordinate]; // Or whatever coordinates...
[self.map addAnnotation:myPin];
Also, you have to return a view for your annotation. You can do so like this:
- (MKAnnotationView *) mapView: (MKMapView *) mapView viewForAnnotation: (id<MKAnnotation>) annotation {
MKPinAnnotationView *pin = (MKPinAnnotationView *) [self.map dequeueReusableAnnotationViewWithIdentifier: #"myPin"];
if (pin == nil) {
pin = [[[MKPinAnnotationView alloc] initWithAnnotation: annotation reuseIdentifier: #"myPin"] autorelease]; // If you use ARC, take out 'autorelease'
} else {
pin.annotation = annotation;
}
pin.animatesDrop = YES;
pin.draggable = YES;
return pin;
}
Then, once you have adopted the MKMapViewDelegate protocol in your controller, you grab the coordinates of a pin after dragging it like this:
- (void)mapView:(MKMapView *)mapView
annotationView:(MKAnnotationView *)annotationView
didChangeDragState:(MKAnnotationViewDragState)newState
fromOldState:(MKAnnotationViewDragState)oldState
{
if (newState == MKAnnotationViewDragStateEnding)
{
CLLocationCoordinate2D droppedAt = annotationView.annotation.coordinate;
NSLog(#"Pin dropped at %f,%f", droppedAt.latitude, droppedAt.longitude);
}
}
Out of the blue, implement the setCoordinates method of your annotation and make a callBAck when your annotation coordinates have changed
edit : beware of the dragState property of your annotation while its moving.
Don't you wan't to do this :
Setting the annotation delegate,
then
- (void)setCoordinate:(CLLocationCoordinate2D)newCoordinate {
coordinate = newCoordinate;
if (self.delegate respondsToSelector:#selector(mapAnnotationCoordinateHaveChanged))
[self.delegate performSelector(#selector(mapAnnotationCoordinateHaveChanged))];
}
In your delegate :
- (void) mapAnnotationCoordinateHaveChanged{
//some coordinates update code
}
- (MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>) annotation
{
MKAnnotationView *a = [ [ MKAnnotationView alloc ] initWithAnnotation:annotation reuseIdentifier:#"currentloc"];
// a.title = #"test";
if ( a == nil )
a = [ [ MKAnnotationView alloc ] initWithAnnotation:annotation reuseIdentifier: #"currentloc" ];
NSLog(#"%f",a.annotation.coordinate.latitude);
NSLog(#"%f",a.annotation.coordinate.longitude);
CLLocation* currentLocationMap = [[[CLLocation alloc] initWithLatitude:a.annotation.coordinate.latitude longitude:a.annotation.coordinate.longitude] autorelease];
[self coordinatesToDMS:currentLocationMap];
MKPinAnnotationView *annView=[[MKPinAnnotationView alloc] initWithAnnotation:a reuseIdentifier:#"currentloc"];
if(a.annotation.coordinate.longitude == mapView.userLocation.coordinate.longitude || a.annotation.coordinate.latitude == mapView.userLocation.coordinate.latitude )
{
if ([annotation isKindOfClass:MKUserLocation.class])
{
//user location view is being requested,
//return nil so it uses the default which is a blue dot...
return nil;
}
//a.image = [UIImage imageNamed:#"userlocation.png"];
//a.pinColor=[UIColor redColor];
}
else
{
// annView.image =[UIImage imageNamed:#"map-pin.png"];
//PinFirst=FALSE;
//annView.pinColor = [UIColor redColor];
// annView.annotation=annotation;
}
annView.animatesDrop = YES;
annView.draggable = YES;
annView.canShowCallout = YES;
return annView;
}
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
{
NSLog(#"map Drag");
}
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view didChangeDragState:(MKAnnotationViewDragState)newState
fromOldState:(MKAnnotationViewDragState)oldState;
{
NSLog(#"pin Drag");
if (newState == MKAnnotationViewDragStateEnding)
{
CLLocationCoordinate2D droppedAt = view.annotation.coordinate;
NSLog(#"Pin dropped at %f,%f", droppedAt.latitude, droppedAt.longitude);
CLLocation* draglocation = [[[CLLocation alloc] initWithLatitude:droppedAt.latitude longitude:droppedAt.longitude] autorelease];
}
}
Example for Swift 2.2, Xcode 7.3.1:
Create custom class adopting MKAnnotation protocol
(note: Swift doesn't have automatic notification for KVO-compliant properties, so I have added my own - this is not required):
import MapKit
class MyAnnotation: NSObject, MKAnnotation {
// MARK: - Required KVO-compliant Property
var coordinate: CLLocationCoordinate2D {
willSet(newCoordinate) {
let notification = NSNotification(name: "VTAnnotationWillSet", object: nil)
NSNotificationCenter.defaultCenter().postNotification(notification)
}
didSet {
let notification = NSNotification(name: "VTAnnotationDidSet", object: nil)
NSNotificationCenter.defaultCenter().postNotification(notification)
}
}
// MARK: - Required Initializer
init(coordinate: CLLocationCoordinate2D) {
self.coordinate = coordinate
}
}
Declare pin view as draggable:
// MARK: - MKMapViewDelegate Actions
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
let pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: pinViewReuseIdentifier)
pinView.draggable = true
return pinView
}
Now, when the pin is dragged, it invokes the delegate method:
func mapView(mapView: MKMapView, didSelectAnnotationView view: MKAnnotationView)
Here, you can retrieve the new end coordinates.
Alternatively, you could create a userInfo dictionary for the custom annotation's coordinate property's notifications. Observers could then retrieve the values upon receipt of the notification.
In Swift my problem was that the coordinate attribute was a letin my subclass and not a var as it is in the MKAnnotation.
class MyAnnotation : NSObject, MKAnnotation {
let title : String?
let subtitle : String?
var coordinate : CLLocationCoordinate2D
init(title: String, subtitle: String, coordinate: CLLocationCoordinate2D) {
self.title = title
self.subtitle = subtitle
self.coordinate = coordinate
super.init()
}
}

MKPinAnnotationView versus MKAnnotationView

I've inherited a project which throws this warning
Incompatible pointer types assigning to 'MKPinAnnotationView *' from 'MKAnnotationView *'
at this line
pinView=[[[MKAnnotationView alloc]initWithAnnotation:annotation reuseIdentifier:defaultPinID]autorelease];
}
I'd like to return the project without warnings so I'm hoping someone here has a quick answer
Full code:
-(MKAnnotationView *)mapView:(MKMapView *)mV viewForAnnotation: (id <MKAnnotation>)annotation {
MKPinAnnotationView *pinView = nil;
NSUserDefaults *prefs=[NSUserDefaults standardUserDefaults];
if(annotation != mapView.userLocation)
{
static NSString *defaultPinID = #"com.invasivecode.pin";
pinView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:defaultPinID];
if (!pinView) {
pinView=[[[MKAnnotationView alloc]initWithAnnotation:annotation reuseIdentifier:defaultPinID]autorelease];
}
}
pinView.animatesDrop=YES;
[mapView.userLocation setTitle:#"I am here"];
[mapView.userLocation setSubtitle:[prefs objectForKey:#"CurrentLocationName"]];
return pinView;
}
Thanks!
The pinView variable is declared as an MKPinAnnotationView but that line creates an MKAnnotationView.
Change this line:
pinView=[[[MKAnnotationView alloc]initWithAnnotation...
to:
pinView=[[[MKPinAnnotationView alloc]initWithAnnotation...
You should also have an else part to that if to handle annotation view re-use:
else
pinView.annotation = annotation;
You are dequeing the pin annotation view and along with that you are allocating the annotation view as your pinview which was technically wrong!!! That's why it was firing the warning I guess. Try this may solve your issue.
-(MKAnnotationView *)mapView:(MKMapView *)mV viewForAnnotation: (id <MKAnnotation>)annotation {
pinView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:defaultPinID];
if (!pinView) {
pinView=[[[MKPinAnnotationView alloc]initWithAnnotation:annotation reuseIdentifier:defaultPinID]autorelease];
}
..........
..........
}

Getting xml data in DetailView from a map view (Iphone IOS)

We are trying to create a mapview with annotations loaded from a xml file. This works so far, and is making use of the KMLViewer code on the apple developer library. Now we are trying to load the data from the XML file into the detailview but only the corresponding entry. So when you click on details on a city for instance, details must be loaded from the xml file of that city.
We are trying for days now but just don't know where to start. We have the following code now:
detailviewcontroller.m
#import "DetailViewController.h"
#implementation DetailViewController
#synthesize address;
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib
- (void)viewDidLoad
{
TabbedCalculationAppDelegate *appDelegate = (TabbedCalculationAppDelegate *)[[UIApplication sharedApplication] delegate];
address.text = appDelegate.addressInput1 ;
[super viewDidLoad];
}
- (void)viewDidUnload
{
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc
{
[super dealloc];
}
Map view
#import "locator.h"
#import "DetailViewController.h"
#implementation locator
#synthesize map, detailViewController, rightButton, customPinView;
- (void)viewDidLoad
{
[super viewDidLoad];
// create a custom navigation bar button and set it to always says "Back"
UIBarButtonItem *temporaryBarButtonItem = [[UIBarButtonItem alloc] init];
temporaryBarButtonItem.title = #"Back";
self.navigationItem.backBarButtonItem = temporaryBarButtonItem;
[temporaryBarButtonItem release];
// Locate the path to the route.kml file in the application's bundle
// and parse it with the KMLParser.
NSString *path = [[NSBundle mainBundle] pathForResource:#"branches" ofType:#"kml"];
kml = [[KMLParser parseKMLAtPath:path] retain];
// Add all of the MKOverlay objects parsed from the KML file to the map.
NSArray *overlays = [kml overlays];
[map addOverlays:overlays];
// Add all of the MKAnnotation objects parsed from the KML file to the map.
NSArray *annotations = [kml points];
[map addAnnotations:annotations];
// Walk the list of overlays and annotations and create a MKMapRect that
// bounds all of them and store it into flyTo.
MKMapRect flyTo = MKMapRectNull;
for (id <MKOverlay> overlay in overlays) {
if (MKMapRectIsNull(flyTo)) {
flyTo = [overlay boundingMapRect];
} else {
flyTo = MKMapRectUnion(flyTo, [overlay boundingMapRect]);
}
}
for (id <MKAnnotation> annotation in annotations) {
MKMapPoint annotationPoint = MKMapPointForCoordinate(annotation.coordinate);
MKMapRect pointRect = MKMapRectMake(annotationPoint.x, annotationPoint.y, 0, 0);
if (MKMapRectIsNull(flyTo)) {
flyTo = pointRect;
} else {
flyTo = MKMapRectUnion(flyTo, pointRect);
}
}
// Position the map so that all overlays and annotations are visible on screen.
MKCoordinateRegion mapRegion;
mapRegion.center.latitude = 51.522416;
mapRegion.center.longitude = 5.141602;
mapRegion.span.latitudeDelta = 5;
mapRegion.span.longitudeDelta = 5;
[map setRegion:mapRegion animated:YES];
}
#pragma mark MKMapViewDelegate
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay
{
return [kml viewForOverlay:overlay];
}
- (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 custom annotations
// // try to dequeue an existing pin view first
static NSString* BridgeAnnotationIdentifier = #"bridgeAnnotationIdentifier";
MKPinAnnotationView* pinView = (MKPinAnnotationView *)
[map dequeueReusableAnnotationViewWithIdentifier:BridgeAnnotationIdentifier];
if (!pinView)
{
// if an existing pin view was not available, create one
customPinView = [[[MKPinAnnotationView alloc]
initWithAnnotation:annotation reuseIdentifier:BridgeAnnotationIdentifier] autorelease];
customPinView.pinColor = MKPinAnnotationColorPurple;
customPinView.animatesDrop = YES;
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;
//
rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
customPinView.rightCalloutAccessoryView = rightButton;
return customPinView;
}else{
return pinView;}
return nil;
}
#pragma mark -
#pragma mark MKMapViewDelegate
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
{
if (view.annotation == mapView.userLocation)
return;
rightButton = (DetailViewController *)view.annotation;
//show detail view using buttonDetail...
}
// the detail view does not want a toolbar so hide it
[self.navigationController setToolbarHidden:YES animated:YES];
[self.navigationController pushViewController:self.detailViewController animated:YES];
}
- (void)viewDidUnload
{
self.detailViewController = nil;
}
- (void)dealloc
{
[detailViewController release];
[super dealloc];
}
#end
As you can see the code is starting to look messy after trying tons of stuff, but we don't really know where to start.
Any help would be extremely appreciated
Thnx in advance!
Please have a look at the interface of the KMLPlacemark in KMLParser, there you can see what is exactly parsed and stored of an xml placemark element. For example the address is missing. So you will have to add all the information you want the parser to gather by implementing the fields in the KMLPlacemark class and alter the KMLParser methods:
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName ...
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName ...
as well as some parts of the KMLPlacemark implementation. To fill the new fields with the parser you'll have to write methods like the - (void)beginName and - (void)endName. It will get a bit tricky when the elements you want to parse have children.
It could be helpful to split the KMLParser file into several files which contain one class each.
If you have achieved that and your placemark contains all the needed details you can catch the tap on an annotation with the MKMapViewDelegate protocol. Implement didDeselectAnnotationView, which could look like this:
- (void) mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)view
{
// implementation example of placemarkForAnnotation below
KMLPlacemark * placemark = [kml placemarkForAnnotation:view.annotation];
MyDetailViewController * myDetailViewController = [[MyDetailViewController alloc] initWithPlacemark:placemark];
[self presentModalViewController:myDetailViewController animated:YES];
[myDetailViewController release];
}
In KMLParser add
- (KMLPlacemark *)placemarkForAnnotation:(id <MKAnnotation>)point
{
// Find the KMLPlacemark object that owns this point and return it
for (KMLPlacemark *placemark in _placemarks) {
if ([placemark point] == point)
return placemark;
}
return nil;
}
Hope I could point you in the right direction. It'll be some work ;)

iPhone MapKit problems: viewForAnnotation inconsistently setting pinColor?

I'm trying setup a map that displays different pin colors depending on the type/class of the location in question. I know this is a pretty common thing to do, but I'm having trouble getting the viewForAnnotation delegate to consistently update/set the pin color.
I have a showThisLocation function that basically cycles through a list of AddressAnnotations and then based on the annotation class (bus stop, hospital, etc.) I set an
if( myClass == 1){
[defaults setObject:#"1" forKey:#"currPinColor"];
[defaults synchronize];
NSLog(#"Should be %#!", [defaults objectForKey:#"currPinColor"]);
} else if( myClass ==2 ){
[defaults setObject:#"2" forKey:#"currPinColor"];
[defaults synchronize];
NSLog(#"Should be %#!", [defaults objectForKey:#"currPinColor"]);
}
[_mapView addAnnotation:myCurrentAnnotation];
then my viewForAnnotation delegate looks like this,
- (MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
if( annotation == mapView.userLocation ){ return nil; }
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
MKPinAnnotationView *annView = nil;
annView = (MKPinAnnotationView*)[mapView dequeueReusableAnnotationViewWithIdentifier:#"currentloc"];
if( annView == nil ){
annView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"currentloc"];
}
annView.pinColor = [defaults integerForKey:#"currPinColor"];
NSLog(#"Pin color: %d", [defaults integerForKey:#"currPinColor"]);
annView.animatesDrop=TRUE;
annView.canShowCallout = YES;
annView.calloutOffset = CGPointMake(-5, 5);
return annView;
}
The problem is that, although the NSLog statements in the "if" block always confirm that the color has been set, the delegate sometimes but not always ends up with the correct color. I've also noticed that what generally happens is that the first search for a new location will set all pins to the last color in the "if" block, but search for the same location again will set the pins to the correct color.
I suspect I am not supposed to usen NSUserDefaults in this way, but I also tried to create my own subclass for MKAnnotation which included an additional property "currentPinColor", and while this allowed me to set the "currentPinColor", when I tried to access the "currentPinColor from the delegate method, the compiler complained that it didn't know anything about "currentPinColor in connection with MKAnnotation. Fair enough I guess, but then I tried to revise the delegate method,
- (MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id <MyCustomMKAnnotation>)annotation
instead of the default
- (MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
at which point the compiler complained that it didn't know anything about the protocol for MyCustomMKAnnotation in this delegate context.
What is the proper way to set the delegate method and/or MyCustomMKAnnotation, or what is the appropriate way to achieve consistent pinColor settings. I'm just about out of ideas for things to try here.
I solved my own problem. Sometimes just formulating the question is enough!
The issue was the way I was trying to access my MKAnnotation delegate.
After posting I happened on these two related posts,
MKPinAnnotationView: Are there more than three colors available?
Mapkit issue in finding annotation current position
The answer to the first post, by user Cannonade, suggests
- (MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>) annotation
{
// ... get the annotation delegate and allocate the MKAnnotationView (annView)
if ([annotationDelegate.type localizedCaseInsensitiveCompare:#"NeedsBluePin"] == NSOrderedSame)
{
UIImage * image = [UIImage imageNamed:#"blue_pin.png"];
UIImageView *imageView = [[[UIImageView alloc] initWithImage:image] autorelease];
[annView addSubview:imageView];
}
// ...
however my issue was that I didn't know how to the get the delegate and it was floating around somewhere in the ... ellipsis part of the above snippet. The answer to the second post, also by user Cannonade, filled this missing bit in,
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
AnnotationDelegate * delegate = (AnnotationDelegate*)view.annotation;
// do stuff with delegate.position;
}
Putting these two snippets together produced the desired result and finally solved my problem.
The working viewForAnnotation delegate method then looks like,
- (MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
if( annotation == mapView.userLocation ){ return nil; }
AddressAnnotation *delegate = annotation; //THIS CAST WAS WHAT WAS MISSING!
MKPinAnnotationView *annView = nil;
annView = (MKPinAnnotationView*)[mapView dequeueReusableAnnotationViewWithIdentifier:#"currentloc"];
if( annView == nil ){
annView = [[MKPinAnnotationView alloc] initWithAnnotation:delegate reuseIdentifier:#"currentloc"];
}
if ([delegate.classPinColor localizedCaseInsensitiveCompare:#"green"] == NSOrderedSame) {
annView.pinColor = MKPinAnnotationColorGreen;
}else if ([delegate.classPinColor localizedCaseInsensitiveCompare:#"purple"] == NSOrderedSame){
annView.pinColor = MKPinAnnotationColorPurple;
}else if ([delegate.classPinColor localizedCaseInsensitiveCompare:#"red"] == NSOrderedSame){
annView.pinColor = MKPinAnnotationColorRed;
}
annView.animatesDrop=TRUE;
annView.canShowCallout = YES;
annView.calloutOffset = CGPointMake(-5, 5);
return annView;
}
Hopefully this will be of help to someone else.
Thanks Cannonade!