Show User's current location on map - ios

I am trying to display user's current location on the map but for some reason, does not takes my default coordinates.
I have a MKMapView class that called inside a UIViewController.
MapView.h
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#import <CoreLocation/CoreLocation.h>
#interface MapView : MKMapView <MKMapViewDelegate, CLLocationManagerDelegate>{
CLLocationManager *locationManager;
}
#end
MapView.m
#import "MapView.h"
#interface MapView ()
#end
#implementation MapView
- (id)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if (self) {
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
self.delegate = self;
[self addAnnotation:[self userLocation]];
[locationManager startUpdatingLocation];
}
return self;
}
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
NSLog(#"didFailWithError: %#", error);
UIAlertView *errorAlert = [[UIAlertView alloc]
initWithTitle:#"Error" message:#"Failed to Get Your Location" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[errorAlert show];
}
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
NSLog(#"didUpdateUserLocation");
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(userLocation.coordinate, 250, 250);
[self setRegion:[self regionThatFits:region] animated:YES];
MKPointAnnotation *point = [[MKPointAnnotation alloc] init];
point.coordinate = userLocation.coordinate;
point.title = #"Where am I?";
point.subtitle = #"I'm here!!!";
[self addAnnotation:point];
}
- (MKAnnotationView*)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation{
NSLog(#"viewForAnnotation");
static NSString *identifier = #"MyAnnotation";
if ([annotation isKindOfClass:[MKUserLocation class]]) {
NSLog(#"Is the user %f, %f", [annotation coordinate].latitude, [annotation coordinate].longitude);
return nil;
}
return nil;
}
- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views{
NSLog(#"didAddAnnotationViews");
for (MKAnnotationView *view in views){
if ([[view annotation] isKindOfClass:[MKUserLocation class]]){
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance([[view annotation] coordinate] , 250, 250);
[mapView setRegion:region animated:YES];
}
}
}
#end
I am getting the output below once access the map.
viewForAnnotation
Is the user 0.000000, 0.000000
didAddAnnotationViews
I can't understand why my didUpdateUserLocation: never get called even if re-submit custom coordinates.
What I am missing here?

You're using a CLLocationManager and setting it's delegate, but your delegate methods are all methods of MKMapViewDelegate.
You can either set the mapview's delegate, or use the locationManager:didUpdateLocations: CLLocationManagerDelegate method.

If it is important to you that the user have the option to turn his location on and off on the map then Nevan King is correct. However, if you are OK with continually showing the user location then no scripting is necessary. Simply click on your mapView in the Storyboard and on the right (in attributes, I think) check the "shows user location" box.

Related

how to show a map with a single annotation and the users location and have the map display both whether the user is 100ft or 100miles

I'd like to have a map with just two points, the users location and a single annotation (it's for a pov). I have included what I
have below. Ideally, I would like it to handle if the user is 100 feet away or 100 miles in displaying both the User and the annotation in
the map view. I think it would be best if the location were at the center and the user were on the edge with some reasonable 20% buffer on the edge of the map view. I only need to support iOS 7 and greater.
Is there something like sizeToFit for annotations that takes into account users position as an annotation?
my MapViewController.h
#interface MapViewController : UIViewController <MKMapViewDelegate, CLLocationManagerDelegate>
#property (weak, nonatomic) IBOutlet MKMapView *mapView;
#property (strong, nonatomic) CLLocationManager *locationManager;
#end
my MapViewController.m:
#define IS_OS_8_OR_LATER ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)
#import "MapViewController.h"
#import "MYUtility.h"
#interface MapViewController (){
NSMutableArray *_yourAnnotationArray;
MKPointAnnotation *_locationPoint;
BOOL _firstTime;
}
#end
#implementation MapViewController
- (void)viewDidLoad {
[super viewDidLoad];
_firstTime=YES;
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
if(IS_OS_8_OR_LATER) {
//[self.locationManager requestWhenInUseAuthorization];
//[self.locationManager requestAlwaysAuthorization];
[self.locationManager requestWhenInUseAuthorization];
[self.locationManager startUpdatingLocation];
}
[self.locationManager startUpdatingLocation];
[self.mapView setShowsUserLocation:YES];
[self.mapView setUserTrackingMode:MKUserTrackingModeFollow animated:YES];
CGFloat latitude=MYLocationLatitude();
CGFloat longitude=MYLocationLongitude();
_locationPoint = [[MKPointAnnotation alloc] init];
_locationPoint.coordinate = CLLocationCoordinate2DMake(latitude, longitude);
_locationPoint.title = #"Where am I?";
_locationPoint.subtitle = #"I'm here!!!";
[self.mapView addAnnotation:_locationPoint];
}
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
}
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
//if(_firstTime){
NSLog(#"here is my thinking %#", [locations lastObject]);
[self setMapStart];
}
-(void)setMapStart
{
//if(_firstTime){
NSLog(#"at top of _firstTime being true");
MKPointAnnotation *myPoint = [[MKPointAnnotation alloc] init];
myPoint.coordinate = CLLocationCoordinate2DMake(34.035645, -118.233434);
MKMapPoint annotationPoint = MKMapPointForCoordinate(self.mapView.userLocation.coordinate);
//[_yourAnnotationArray addObject:annotationPoint];
_yourAnnotationArray=[[NSMutableArray alloc] initWithObjects:_locationPoint,annotationPoint, nil];
//NSLog(#"here is my count: %i",(unsigned long)[_yourAnnotationArray count]);
[self.mapView showAnnotations:self.mapView.annotations animated:YES]; // <- determine when this has run
_firstTime=NO;
//}
}

Drop Pin on current location

I am building an iOS app and need to be able to place a pin where the user currently is! For some reason I have been having an awful time getting it to work! I tried the following code but was faced with an error.
- (MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>) annotation{
if (annotation == mapView.userLocation)
{
MKPinAnnotationView *annView=[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:nil];
annView.pinColor = MKPinAnnotationColorRed;
annView.animatesDrop=TRUE;
annView.canShowCallout = YES;
annView.calloutOffset = CGPointMake(-5, 5);
return annView;
[annView release];
}
}
The error was:
Control May Reach End of non-void function.
Thank you so much for the help! '
Appreciate it!
The following code works fine in the Xcode iPhone simulator:
#import "ViewController.h"
#import MapKit;
#interface ViewController () <MKMapViewDelegate>
#property (weak, nonatomic) IBOutlet MKMapView *mapView;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self.mapView setDelegate:self];
[self.mapView setShowsUserLocation:YES];
}
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation {
// zoom to region containing the user location
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(userLocation.coordinate, 800, 800);
[self.mapView setRegion:[self.mapView regionThatFits:region] animated:YES];
// add the annotation
MKPointAnnotation *point = [[MKPointAnnotation alloc] init];
point.coordinate = userLocation.coordinate;
point.title = #"The Location";
point.subtitle = #"Sub-title";
[self.mapView addAnnotation:point];
}
#end
When a method has a return type then something must be returned in all conditions, you are only returning something in the if branch, not otherwise. The compiler requires you to also return something when the if statement evaluates to false - that is the reason why you are getting the compilation error.
Incidentally the line
[annView release];
can never get executed because you are returning before then. But is there any reason why you are calling this? i.e. attempting to use non ARC code? Have you copied this line from somewhere (which is old code)

How to add callout into individual annotation in map view

I'm trying to set each annotation with different callout detail info.
Currently when I click on any annotation location, it shows all the callout info.
#import "AnnotationViewController.h"
#import "Annotation.h"
#implementation AnnotationViewController
#synthesize mapView;
-(void)viewDidLoad {
[super viewDidLoad];
[mapView setMapType:MKMapTypeStandard];
[mapView setZoomEnabled:YES];
[mapView setScrollEnabled:YES];
[mapView setDelegate:self];
MKCoordinateRegion TT = { {0.0, 0.0} , {0.0, 0.0} };
TT.center.latitude = 43.65343;
TT.center.longitude = -79.396311;
TT.span.longitudeDelta = 0.02f;
TT.span.latitudeDelta = 0.02f;
[mapView setRegion:TT animated:YES];
Annotation *ann1 = [[Annotation alloc] init];
ann1.title = #"Annotation 01";
ann1.subtitle = #"Message 01";
ann1.coordinate = TT.center;
[mapView addAnnotation:ann1];
MKCoordinateRegion TTY = { {0.0, 0.0} , {0.0, 0.0} };
TTY.center.latitude = 43.76919;
TTY.center.longitude = -79.41245;
TTY.span.longitudeDelta = 0.02f;
TTY.span.latitudeDelta = 0.02f;
[mapView setRegion:TTY animated:YES];
Annotation *ann2 = [[Annotation alloc] init];
ann2.title = #"Annotation 02";
ann2.subtitle = #"Message 02";
ann2.coordinate = TTY.center;
[mapView addAnnotation:ann2];
}
-(MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:id<MKAnnotation>)annotation
{
MKPinAnnotationView *view = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"pin"];
view.pinColor = MKPinAnnotationColorPurple;
view.enabled = YES;
view.animatesDrop = YES;
view.canShowCallout = YES;
UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"GPSicon.png"]];
view.leftCalloutAccessoryView = imageView;
view.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
return view;
}
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
NSString *msg = [#"Location 01" stringByAppendingFormat:#"Opening 01"];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Plaza 01" message:msg delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
NSString *msg02 = [#"Location 02" stringByAppendingFormat:#"Opening 02"];
UIAlertView *alert02 = [[UIAlertView alloc] initWithTitle:#"Plaza 02" message:msg02 delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert02 show];
}
-(void)button:(id)sender {
NSLog(#"Button action");
}
- (void)dealloc
{
}
- (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
/*
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
[super viewDidLoad];
}
*/
- (void)viewDidUnload
{
[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);
}
end
So this is what's happening: When I click on "Annotation 01" the bubble will appear. But when I click on the callout "detail icon", it will popup both Location 01 & Location 2 titles.
Thanks Anna for you help. But I still get 2 callouts when i check one annotation. This is the code now..
#import "AnnotationViewController.h"
#import "Annotation.h"
#implementation AnnotationViewController
#synthesize mapView;
-(void)viewDidLoad {
[super viewDidLoad];
[mapView setMapType:MKMapTypeStandard];
[mapView setZoomEnabled:YES];
[mapView setScrollEnabled:YES];
[mapView setDelegate:self];
MKCoordinateRegion TT = { {0.0, 0.0} , {0.0, 0.0} };
TT.center.latitude = 43.65343;
TT.center.longitude = -79.396311;
TT.span.longitudeDelta = 0.02f;
TT.span.latitudeDelta = 0.02f;
[mapView setRegion:TT animated:YES];
Annotation *ann1 = [[Annotation alloc] init];
ann1.title = #"Annotation 01";
ann1.subtitle = #"Message 01";
ann1.coordinate = TT.center;
[mapView addAnnotation:ann1];
MKCoordinateRegion TTY = { {0.0, 0.0} , {0.0, 0.0} };
TTY.center.latitude = 43.76919;
TTY.center.longitude = -79.41245;
TTY.span.longitudeDelta = 0.02f;
TTY.span.latitudeDelta = 0.02f;
[mapView setRegion:TTY animated:YES];
Annotation *ann2 = [[Annotation alloc] init];
ann2.title = #"Annotation 02";
ann2.subtitle = #"Message 02";
ann2.coordinate = TTY.center;
[mapView addAnnotation:ann2];
}
-(MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:id<MKAnnotation>)annotation
{
MKPinAnnotationView *view = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"pin"];
view.pinColor = MKPinAnnotationColorPurple;
view.enabled = YES;
view.animatesDrop = YES;
view.canShowCallout = YES;
UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"GPSicon.png"]];
view.leftCalloutAccessoryView = imageView;
view.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
return view;
}
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
//first make sure the annotation is our custom class...
if ([view.annotation isKindOfClass:[Annotation class]])
{
//cast the object to our custom class...
Annotation *ann1 = (Annotation *)view.annotation;
//show one alert view with title set to annotation's title
//and message set to annotation's subtitle...
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:ann1.title
message:ann1.subtitle
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
}
//first make sure the annotation is our custom class...
if ([view.annotation isKindOfClass:[Annotation class]])
{
//cast the object to our custom class...
Annotation *ann2 = (Annotation *)view.annotation;
//show one alert view with title set to annotation's title
//and message set to annotation's subtitle...
UIAlertView *alert2 = [[UIAlertView alloc]
initWithTitle:ann2.title
message:ann2.subtitle
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
}
}
-(void)button:(id)sender {
NSLog(#"Button action");
}
- (void)dealloc
{
}
- (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
/*
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
[super viewDidLoad];
}
*/
- (void)viewDidUnload
{
[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);
}
end
My Annotation.m file code
#import "Annotation.h"
#implementation Annotation
#synthesize coordinate, title, subtitle;
-(void)dealloc {
}
#end
My Annotation.h file code
#import <Foundation/Foundation.h>
#import <MapKit/MKAnnotation.h>
#interface Annotation : NSObject <MKAnnotation> {
CLLocationCoordinate2D coordinate;
NSString *title;
NSString *subtitle;
}
#property(nonatomic, assign) CLLocationCoordinate2D coordinate;
#property(nonatomic, copy) NSString *title;
#property(nonatomic, copy) NSString *subtitle;
#end
Do i have to make alot of h/m files for each Annotation to make the callout different? im just stomp at this point. any guesses would be great, thanks!
In calloutAccessoryControlTapped, the code is showing two alert views with hard-coded text for the title and message one after the other. So that's what you get obviously.
In the calloutAccessoryControlTapped method, the annotation object whose callout button was tapped is available via view.annotation.
The view parameter is the MKAnnotationView which has an annotation property that points to its related annotation.
So change the code to show only one alert view and with the title and message based on the properties of the annotation object.
It's also a good idea to check the class of the annotation to make sure it's the type of annotation you're expecting.
For example:
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
//first make sure the annotation is our custom class...
if ([view.annotation isKindOfClass:[Annotation class]])
{
//cast the object to our custom class...
Annotation *ann = (Annotation *)view.annotation;
//show one alert view with title set to annotation's title
//and message set to annotation's subtitle...
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:ann.title
message:ann.subtitle
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
}
}

Open Detail View from Annotation

I have a map view with several annotations. Each annotation has a button that takes the user to a detail view for that annotation. The detail view currently has two labels, one for the title from the annotation and the other for the subtitle of the annotation, and a "Get Directions" button. I want the title label to be populated with the title from the annotation and the subtitle label to be populated with the subtitle from the annotation. I want the Get Directions button to take the user into the Maps app and give them directions to the address from their current location. I'd also like to add website and phone number to the detail view but I'm not sure how to populate those fields either. The detail view is called AnnotationDetailView but I haven't done anything with it besides add the button and labels in IB. I'm just not sure how to populate the labels and get the button doing what I want. Any help would be great.
Here is my code:
//
// RSFM.m
#import "RSFM.h"
#import "AnnotationDetailView.h"
#interface RSFM ()
#end
#implementation RSFM
{
}
#synthesize centerCoordinate, coordinate, title, subtitle;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self)
{
self.title = NSLocalizedString(#"Farm Markets", #"Farm Markets");
// Create location manager object
locationManager = [[CLLocationManager alloc]init];
[locationManager setDelegate:self];
// And we want it to be as accurate as possible regardless of how much time/power it takes
[locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
// Tell our manager to start looking for its location immediately
// [locationManager startUpdatingLocation];
}
return self;
}
/*
- (void)setCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate
{
centerCoordinate = CLLocationCoordinate2DMake(37.7885, 85.3279);
}
*/
- (void)findLocation
{
[locationManager startUpdatingLocation];
[activityIndicator startAnimating];
// [locationTitleField setHidden:YES];
[locationManager stopUpdatingLocation];
}
- (void)foundLocation:(CLLocation *)loc
{
CLLocationCoordinate2D coord = [loc coordinate];
// Create an instance of BNRMapPoint with the current data
// BNRMapPoint *mp = [[BNRMapPoint alloc]initWithCoordinate:coord title:[locationTitleField text]];
// Add it to the map view
// [worldView addAnnotation:mp];
// Zoom the region to this location
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(coord, 700000, 700000);
[worldView setRegion:region animated:YES];
// Reset the UI
// [locationTitleField setText:#""];
[activityIndicator stopAnimating];
// [locationTitleField setHidden:NO];
[locationManager stopUpdatingLocation];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
NSLog(#"%#", newLocation);
// How many seconds ago was this new location created?
NSTimeInterval t = [[newLocation timestamp]timeIntervalSinceNow];
// CLLocationManagers will return the last found location of the device first, you don't want that data in this case.
// If this location was made more than 3 minutes ago, ignore it.
if (t < -180)
{
// this is cached data, you don't want it, keep looking
return;
}
[self foundLocation:newLocation];
}
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
NSLog(#"Could not find location: %#", error);
}
- (void)dealloc
{
// Tell the location manager to stop sending us messages
[locationManager setDelegate:nil];
}
- (IBAction)showDetails:(id)sender
{
AnnotationDetailView *detail = [[AnnotationDetailView alloc] initWithNibName:nil bundle:nil];
detail.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self.navigationController pushViewController:detail animated:YES];
}
- (MKAnnotationView*)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
// If it's the user location, return nil
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil;
// Try to dequeue an existing pin view first
static NSString *annotationIdentifier = #"AnnotationIdentifier";
MKPinAnnotationView *pinView = [[MKPinAnnotationView alloc]initWithAnnotation:annotation reuseIdentifier:annotationIdentifier];
pinView.animatesDrop = YES;
pinView.canShowCallout = YES;
UIButton *rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
[rightButton setTitle:annotation.title forState:UIControlStateNormal];
[rightButton addTarget:self action:#selector(showDetails:) forControlEvents:UIControlEventTouchUpInside];
pinView.rightCalloutAccessoryView = rightButton;
return pinView;
}
- (void)viewDidLoad
{
[locationManager startUpdatingLocation];
[worldView setShowsUserLocation:YES];
[locationManager stopUpdatingLocation];
NSMutableArray *marketLocations = [[NSMutableArray alloc]init];
NSMutableArray *lat = [[NSMutableArray alloc]initWithObjects:#"37.7867266", #"37.0703517", #"37.1610806", #"37.318367", #"37.3559204", #"37.4154066", #"37.4757622", #"37.7450252", #"37.6318978", #"37.0716803", nil];
NSMutableArray *lon = [[NSMutableArray alloc]initWithObjects:#"-87.608209", #"-88.1237899", #"-87.9148629", #"-87.5074402", #"-87.5448032", #"-87.8003148", #"-87.9515986", #"-87.9061638", #"-87.1148574", #"-87.3008418", nil];
NSMutableArray *title1 = [[NSMutableArray alloc]initWithObjects:#"Cates Farm", #"Broadbent B & B Foods", #"Cayce's Pumpkin Patch", #"Metcalfe Landscaping", #"Brumfield Farm Market", #"Dogwood Valley Farm", #"Country Fresh Meats & Farmers Market", #"Jim David Meats", #"Trunnell's Farm Market", #"Lovell's Orchard & Farm Market", nil];
NSMutableArray *subtitle1 = [[NSMutableArray alloc]initWithObjects:#"Hwy 425 Henderson, KY 42420", #"257 Mary Blue Road Kuttawa, KY 42055", #"153 Farmersville Road Princeton, KY 42445", #"410 Princeton Road Madisonville, KY 42431", #"3320 Nebo Road Madisonville, KY 42431", #"4551 State Route 109N Clay, KY 42404", #"9355 US Hwy 60 W Sturgis, KY 42459",#"350 T. Frank Wathen Rd. Uniontown, KY 42461", #"9255 Hwy 431 Utica, KY 42376", #"22850 Coal Creek Road Hopkinsville, KY 42240", nil];
CLLocationCoordinate2D location;
MKPointAnnotation *marketAnnotation;
for (int x = 0; x < [lat count]; x++)
{
marketAnnotation = [[MKPointAnnotation alloc]init];
location.latitude = [[lat objectAtIndex:x]floatValue];
location.longitude = [[lon objectAtIndex:x]floatValue];
marketAnnotation.coordinate = location;
marketAnnotation.title = [title1 objectAtIndex:x];
marketAnnotation.subtitle = [subtitle1 objectAtIndex:x];
[marketLocations addObject:marketAnnotation];
}
[worldView addAnnotations:marketLocations];
/*
MKPointAnnotation *point = [[MKPointAnnotation alloc] init];
point.coordinate = CLLocationCoordinate2DMake(37.7867266, -87.608209);
point.title = #"Cates Farm";
point.subtitle = #"Hwy 425 Henderson, KY 42420";
[worldView addAnnotation:point];
*/
}
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
CLLocationCoordinate2D loc = [userLocation coordinate];
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(loc, 700000, 700000);
[worldView setRegion:region animated:YES];
[locationManager stopUpdatingLocation];
locationManager.delegate = nil;
}
- (IBAction)selectSegmentControl
{
int segmentTouched = [mapVarieties selectedSegmentIndex];
NSString *segmentName = [mapVarieties titleForSegmentAtIndex:segmentTouched];
if ([segmentName isEqualToString:#"Street"])
{
[worldView setMapType:MKMapTypeStandard];
}
if ([segmentName isEqualToString:#"Satellite"])
{
[worldView setMapType:MKMapTypeSatellite];
}
if ([segmentName isEqualToString:#"Hybrid"])
{
[worldView setMapType:MKMapTypeHybrid];
}
}
#end
Here is where I show the detail view
- (IBAction)showDetails:(id)sender
{
AnnotationDetailView *detail = [[AnnotationDetailView alloc] initWithNibName:nil bundle:nil];
detail.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
NSLog(#"%#", marketAnnotation.title);
detail.ti.text = marketAnnotation.title;
[self.navigationController pushViewController:detail animated:YES];
}
and the AnnotationDetailView
//
// AnnotationDetailView.m
#import "AnnotationDetailView.h"
#interface AnnotationDetailView ()
#end
#implementation AnnotationDetailView
#synthesize ti;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
// [title setText:marketAnnotation.title];
// [subtitle setText:marketAnnotation.subtitle];
ti = [[UILabel alloc]initWithFrame:CGRectMake(20, 20, 281, 21)];
[self.view addSubview:ti];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
I'm having a couple of issues with this. One, the label is not showing up. Two, I have an NSLog statement in my showDetails method to show me what marketAnnotations is. It is showing the title of the last annotation added.
You can add extra parameters to your MKAnnotation class. For example this Annotation class uses 3 extra strings to hold information:
// MyAnnotation.h
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface MyAnnotation : NSObject <MKAnnotation>
#property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
#property (nonatomic, copy, readonly) NSString *title;
#property (nonatomic, copy, readonly) NSString *subtitle;
#property (nonatomic, copy, readonly) NSString *myString1;
#property (nonatomic, copy, readonly) NSString *myString2;
#property (nonatomic, copy, readonly) NSString *myString3;
-(id)initWithCoordinates:(CLLocationCoordinate2D) paramCoordinates
title:(NSString *) paramTitle
subTitle:(NSString *) paramSubTitle
myString1:(NSString *) paramMyString1
myString2:(NSString *) paramMyString2
myString3:(NSString *) paramMyString3;
#end
// MyAnnotation.m
#import "MyAnnotation.h"
#implementation MyAnnotation
-(id)initWithCoordinates:(CLLocationCoordinate2D)paramCoordinates
title:(NSString *)paramTitle
subTitle:(NSString *)paramSubTitle
myString1:(NSString *)paramMyString1
myString2:(NSString *)paramMyString2
myString3:(NSString *)paramMyString1
{
self = [super init];
if(self != nil)
{
_coordinate = paramCoordinates;
_title = paramTitle;
_subtitle = paramSubTitle;
_myString1 = paramMyString1;
_myString2 = paramMyString2;
_myString3 = paramMyString3;
}
return (self);
}
#end
When the user taps a pin, you can do this to pass information from the Annotation to the next View Controller:
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
if ([(UIButton*)control buttonType] == UIButtonTypeDetailDisclosure)
{
YourViewController *vc = [[YourViewController alloc] init];
[vc setTitleString:[(MyAnnotation *)view.annotation title]];
[vc setSubTitleString:[(MyAnnotation *)view.annotation subTitle]];
[vc setPassedString1:[(MyAnnotation *)view.annotation myString1]];
[vc setPassedString2:[(MyAnnotation *)view.annotation myString2]];
[vc setPassedString3:[(MyAnnotation *)view.annotation myString3]];
[[self navigationController] pushViewController:vc animated:YES];
}
}
In your next View Controller (vc), you can modify the button text like this:
[myButton setTitle:myPassedString1 forState:UIControlStateNormal];

iOS SDK - MapKit MKAnnotationView questions - location and multiple annotations?

Progressing apace, trying to learn in and outs of MapKit and annotations.
I've made some good progress, but I'm having problems when I attempt to add location services to the mix.
If I return nil to the
- (MKAnnotationView *)mapView:(MKMapView *)mapView
viewForAnnotation:(id < MKAnnotation >)annotation
method, I get a result VERY close to what I'm looking for, I have my annotation pins, I have my user's location showing in a standard way.
But if I return myPins (with all the parameters set as below), I get the correct behavior on all the pins but the properties are also set for the user's location (it's a red pin, it drops into place every time the view moves or a pin is selected). Annoying.
How does one set the properties for the annotations separately from the location? Is there a way to return multiple MKAnnotationViews? I know I'm missing some basic concept, but would appreciate any help you can give me. Thanks in advance.
Here's my main viewController, in total:
//
// ViewController.m
// mapFun
//
#import "ViewController.h"
#implementation ViewController
#synthesize segmentedControl;
#synthesize mapView,location, myLocations, myAnnotation, region;
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.mapView.delegate = self;
//location manager stuff
CLLocationManager *locationManager = [[CLLocationManager alloc] init];
[locationManager setDelegate:self];
[locationManager setDistanceFilter:kCLDistanceFilterNone];
[locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
if([CLLocationManager locationServicesEnabled])
{
[self.mapView setShowsUserLocation:YES];
[locationManager startUpdatingHeading];
}
[self loadUpLocations];
//set first zoom center and span, to zoom from to initial zoom
region.center=CLLocationCoordinate2DMake(
33.75070416856952,
-84.37034368515015);
MKCoordinateSpan span;
span.latitudeDelta=.3;
span.longitudeDelta=.3;
region.span=span;
[mapView setRegion:region animated:YES];
}
- (void)viewDidUnload
{
[self setMapView:nil];
[self setSegmentedControl:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
//zoom to initial zoom
[self setInitialZoom];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
//Load up a buncha locations
-(void)loadUpLocations{
myLocations = [LoadObjectsFromFile loadFromFile:#"locations2" ofType:#"plist"];
//loop through and make annotations
for (NSString *loc in myLocations) {
NSDictionary *value =[myLocations objectForKey:loc];
//create instance of MapAnnotations
CLLocationCoordinate2D temp =
CLLocationCoordinate2DMake(
[[value objectForKey:#"latitude"] doubleValue],
[[value objectForKey:#"longitude"] doubleValue]);
myAnnotation = [[MapAnnotations alloc]initWithLocation:temp];
myAnnotation.title = [value objectForKey:#"title"];
myAnnotation.subtitle = [value objectForKey:#"subtitle"];
myAnnotation.info =[value objectForKey:#"info"];
[self.mapView addAnnotation:myAnnotation];
}
}
- (void)setInitialZoom{
// MKCoordinateRegion region;
region.center = CLLocationCoordinate2DMake(33.77, -84.37);
//set level of zoom
MKCoordinateSpan span;
span.latitudeDelta=.04;
span.longitudeDelta=.04;
region.span=span;
[mapView setRegion:region animated:YES];
}
// used when selecting annotations
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view{
MKPointAnnotation *selectedAnnotation = view.annotation;
NSLog(#"The annotation selected is %#.",selectedAnnotation.title);
//set level of zoom
CLLocationCoordinate2D newCenter =
CLLocationCoordinate2DMake(
selectedAnnotation.coordinate.latitude,
selectedAnnotation.coordinate.longitude);
MKCoordinateSpan span;
span.latitudeDelta=.02;
span.longitudeDelta=.02;
MKCoordinateRegion zoomRegion = MKCoordinateRegionMake(newCenter, span);
[ self.mapView setRegion:zoomRegion animated:YES];
}
- (void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)view
{
// [self zoomOutToInitial:nil];
}
- (IBAction)zoomOutToInitial:(id)sender {
[self.mapView setRegion:region animated:YES];
}
- (IBAction)segChanged:(id)sender {
if (self.segmentedControl.selectedSegmentIndex==0) {
[self.mapView setMapType:MKMapTypeStandard];
}
if (self.segmentedControl.selectedSegmentIndex==1) {
[self.mapView setMapType:MKMapTypeSatellite];
}
if (self.segmentedControl.selectedSegmentIndex==2) {
[self.mapView setMapType:MKMapTypeHybrid];
}
}
// This gets called every time an annotation is in the map view
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id < MKAnnotation >)annotation
{
NSLog(#"Map view can see annotation %#.",annotation.title);
MKPinAnnotationView *myPins;
if (!myPins) {
myPins= [[MKPinAnnotationView alloc]initWithAnnotation:self.myAnnotation
reuseIdentifier:#"myPins"];
}
myPins.animatesDrop = YES;
myPins.canShowCallout = YES;
myPins.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
return nil;
//return myPins;
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
[manager stopUpdatingHeading];
}
#end
Answering my own question. Thanks anyway.
I finally found the answer in a useful document called "Location Awareness Programming Guide," from Apple.
Here's the corrected MKAnnotationView method:
// This gets called every time an annotation is in the map view
- (MKAnnotationView *)mapView:(MKMapView *)theView
viewForAnnotation:(id < MKAnnotation >)annotation
{
NSLog(#"Map view can see annotation %#.",annotation.title);
//if the annotation is a user location
if ([annotation isKindOfClass:[MKUserLocation class]]) {
return nil;
}
//uses my custom class
if ([annotation isKindOfClass:[MapAnnotations class]]) {
// try for reuse of pins
MKPinAnnotationView *myPins = (MKPinAnnotationView *)
[theView dequeueReusableAnnotationViewWithIdentifier:#"myPins"];
if (!myPins) {
myPins= [[MKPinAnnotationView alloc]initWithAnnotation:annotation
reuseIdentifier:#"myPins"];
myPins.animatesDrop = YES;
myPins.canShowCallout = YES;
myPins.rightCalloutAccessoryView =
[UIButton buttonWithType:UIButtonTypeDetailDisclosure];
}else
myPins.annotation = annotation;
return myPins;
}
return nil;
}
The basic concept is to return if and only if the view asked is actually the userlocation view.
You simply test if the Annotation forwarded to you is of userlocation class, like this:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id < MKAnnotation >)annotation
{
if ([annotation isKindOfClass:[MKUserLocation class]])
        return nil;
// do your usual stuff
}

Resources