I am trying to provide directions to certain areas I have marked with map pins on my map view. On those pins I have displayed the name of the location and where it is located. I would like for them to be able to click the "i" that is usually on some Apple "pop ups" and it direct them to the Maps app and give them directions based on the coordinates I programmed in to place the map pin where it is. I will post two pictures first of what I have and what I would like to accomplish.
What I have right now
What I want to add to my pin (just the "i")
Now I will post my code of how I accomplished my first screenshot.
ViewController.h:
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#interface ViewController : UIViewController
{
MKMapView *mapView;
}
#property (nonatomic, retain) IBOutlet MKMapView *mapView;
-(IBAction)SetMap:(id)sender;
-(IBAction)GetLocation:(id)sender;
-(IBAction)Directions:(id)sender;
#end
ViewController.m:
#import "ViewController.h"
#import "MapPin.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize mapView;
- (void)viewDidLoad
{
[super viewDidLoad];
//Moss Preserve coordinates
MKCoordinateRegion MossPreserveRegion = { {0.0, 0.0}, {0.0, 0.0}};
MossPreserveRegion.center.latitude = 33.3816566;
MossPreserveRegion.center.longitude = -86.8415451;
MossPreserveRegion.span.longitudeDelta = 55.0;
MossPreserveRegion.span.latitudeDelta = 55.0;
[mapView setRegion:MossPreserveRegion animated:YES];
//Moss Preserve annotation and map pin
MapPin *MossPreserveAnnotation = [[MapPin alloc] init];
MossPreserveAnnotation.title = #"Moss Rock Preserve Boulder Fields";
MossPreserveAnnotation.subtitle = #"Hoover, AL";
MossPreserveAnnotation.coordinate = MossPreserveRegion.center;
[mapView addAnnotation:MossPreserveAnnotation];
//Horse Pens 40 coordinates
MKCoordinateRegion HorsePenRegion = { {0.0, 0.0}, {0.0, 0.0}};
HorsePenRegion.center.latitude = 33.9207535;
HorsePenRegion.center.longitude = -86.3089447;
HorsePenRegion.span.longitudeDelta = 55.0;
HorsePenRegion.span.latitudeDelta = 55.0;
[mapView setRegion:HorsePenRegion animated:YES];
//Horse Pens 40 annotation and map pin
MapPin *HorsePenAnnotation = [[MapPin alloc] init];
HorsePenAnnotation.title = #"Horse Pens 40";
HorsePenAnnotation.subtitle = #"Steele, AL ";
HorsePenAnnotation.coordinate = HorsePenRegion.center;
[mapView addAnnotation:HorsePenAnnotation];
// Create an MKMapItem to pass to the Maps app
MKPlacemark *placemark = [[MKPlacemark alloc] initWithCoordinate:MossPreserveAnnotation.coordinate
addressDictionary:nil];
MKMapItem *mossPreserveMapItem = [[MKMapItem alloc] initWithPlacemark:placemark];
[mossPreserveMapItem setName:MossPreserveAnnotation.title];
NSDictionary *launchOptions = #{MKLaunchOptionsDirectionsModeKey : MKLaunchOptionsDirectionsModeDriving};
// Get the "Current User Location" MKMapItem
placemark = [[MKPlacemark alloc] initWithCoordinate:HorsePenRegion.coordinate
addressDictionary:nil];
MKMapItem *horsePenMapItem = [[MKMapItem alloc] initWithPlacemark:placemark];
horsePenmapItem.name = HorsePenRegion.title;
// Pass the current location and destination map items to the Maps app
// Set the direction mode in the launchOptions dictionary
[MKMapItem openMapsWithItems:#[mossPreserveMapItem, horsePenmapItem]
launchOptions:launchOptions];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(IBAction)SetMap:(id)sender;
{
switch (((UISegmentedControl *) sender).selectedSegmentIndex)
{
case 0:
mapView.mapType = MKMapTypeStandard;
break;
case 1:
mapView.mapType = MKMapTypeSatellite;
break;
case 2:
mapView.mapType = MKMapTypeHybrid;
break;
default:
break;
}
}
-(IBAction)GetLocation:(id)sender;
{
mapView.showsUserLocation = YES;
}
-(IBAction)Directions:(id)sender;
{
NSString *urlString = #"http://maps.apple.com/maps?daddr=33.3816566,-86.8415451";
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:urlString]];
}
#end
MapPin.h:
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface MapPin : NSObject <MKAnnotation>
{
CLLocationCoordinate2D coordinate;
NSString *title;
NSString *subtitle;
}
#property (nonatomic, assign) CLLocationCoordinate2D coordinate;
#property (nonatomic, copy) NSString *title;
#property (nonatomic, copy) NSString *subtitle;
#end
MapPin.m:
#import "MapPin.h"
#implementation MapPin
#synthesize coordinate, title, subtitle;
#end
I see this all of the time in apps that I use. i just don't even really know what it is called, so I could even search for it. I am not looking for someone to hold my hand and tell me the answer, just some proper guidance and constructive criticism.
Thanks
Is this what you need?
#import "MapViewController.h"
#import "MapPin.h"
#import MapKit;
#interface MapViewController ()<MKMapViewDelegate>
#property (nonatomic, weak) IBOutlet MKMapView *mapView;
#end
#implementation MapViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
_mapView.delegate = self;
//Moss Preserve coordinates
MKCoordinateRegion MossPreserveRegion = { {0.0, 0.0}, {0.0, 0.0}};
MossPreserveRegion.center.latitude = 33.3816566;
MossPreserveRegion.center.longitude = -86.8415451;
MossPreserveRegion.span.longitudeDelta = 55.0;
MossPreserveRegion.span.latitudeDelta = 55.0;
[_mapView setRegion:MossPreserveRegion animated:YES];
//Moss Preserve annotation and map pin
MapPin *MossPreserveAnnotation = [[MapPin alloc] init];
MossPreserveAnnotation.title = #"Moss Rock Preserve Boulder Fields";
MossPreserveAnnotation.subtitle = #"Hoover, AL";
MossPreserveAnnotation.coordinate = MossPreserveRegion.center;
[_mapView addAnnotation:MossPreserveAnnotation];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
// Create an MKMapItem to pass to the Maps app
MKPlacemark *placemark = [[MKPlacemark alloc] initWithCoordinate:view.annotation.coordinate
addressDictionary:nil];
MKMapItem *mossPreserveMapItem = [[MKMapItem alloc] initWithPlacemark:placemark];
[mossPreserveMapItem setName:view.annotation.title];
NSDictionary *launchOptions = #{MKLaunchOptionsDirectionsModeKey : MKLaunchOptionsDirectionsModeDriving};
// Get the "Current User Location" MKMapItem
MKMapItem *currentLocationItem = [MKMapItem mapItemForCurrentLocation];
[MKMapItem openMapsWithItems:#[mossPreserveMapItem, currentLocationItem]
launchOptions:launchOptions];
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
MKAnnotationView *annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation
reuseIdentifier:#"MKPinAnnotationView"];
annotationView.canShowCallout = YES;
UIButton *detailButton = [UIButton buttonWithType:UIButtonTypeInfoLight];
annotationView.rightCalloutAccessoryView = detailButton;
return annotationView;
}
You need to create 2 map items for your locations and pass as arguments to MKMapItem's openMapsWithItems:launchOptions:
// Create an MKMapItem to pass to the Maps app
MKPlacemark *placemark = [[MKPlacemark alloc] initWithCoordinate:MossPreserveAnnotation.coordinate
addressDictionary:nil];
MKMapItem *mapItem1 = [[MKMapItem alloc] initWithPlacemark:placemark];
[mapItem setName:MossPreserveAnnotation.title];
NSDictionary *launchOptions = #{MKLaunchOptionsDirectionsModeKey : MKLaunchOptionsDirectionsModeDriving};
// Get the "Current User Location" MKMapItem
placemark = [[MKPlacemark alloc] initWithCoordinate:HorsePenAnnotation.coordinate
addressDictionary:nil];
MKMapItem *mapItem2 = [[MKMapItem alloc] initWithPlacemark:placemark];
mapItem2.name = HorsePenAnnotation.title;
// Pass the current location and destination map items to the Maps app
// Set the direction mode in the launchOptions dictionary
[MKMapItem openMapsWithItems:#[mapItem1, mapItem2]
launchOptions:launchOptions];
Related
I am trying to create a custom annotation for a simple map view of mine, but the annotaiton pin is not showing up on the map when i run the simulator.
I have a MapViewAnnotation class which implements the MKAnnotation protocol, and I have another MapViewController class where I programmatically create a mapView and display the map.
MapViewAnnotation.h :
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface MapViewAnnotation : NSObject <MKAnnotation>
#property (nonatomic, copy) NSString *title;
#property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
-(id) initWithTitle: (NSString *) title AndCoordinate: (CLLocationCoordinate2D) coordinate;
#end
MapViewAnnotation.m :
#import "MapViewAnnotation.h"
#implementation MapViewAnnotation
#synthesize coordinate = _coordinate;
#synthesize title = _title;
-(id) initWithTitle:(NSString *)title AndCoordinate:(CLLocationCoordinate2D)coordinate{
self = [super init];
_title = title;
_coordinate = coordinate;
return self;
}
#end
MapViewController.h :
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#interface MapViewController : UIViewController
#property (weak, nonatomic) MKMapView *mapView;
-(void) goToLocation;
#end
MapViewController.m :
#import "MapViewController.h"
#import "MapViewAnnotation.h"
#define LATITUDE 42.3889;
#define LONGITUDE -72.5278;
#interface MapViewController ()
#end
#implementation MapViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.mapView = [[MKMapView alloc] initWithFrame:self.view.frame];
[self.view addSubview:self.mapView];
[self goToLocation];
[self.mapView addAnnotations:[self createAnnotations]];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void) goToLocation {
MKCoordinateRegion newRegion;
newRegion.center.latitude = LATITUDE;
newRegion.center.longitude = LONGITUDE;
newRegion.span.latitudeDelta = 0.05f;
newRegion.span.longitudeDelta = 0.05f;
self.mapView.showsPointsOfInterest = YES;
[self.mapView setRegion:newRegion animated:YES];
}
-(NSMutableArray *) createAnnotations {
NSMutableArray *annotations = [[NSMutableArray alloc] init];
NSString *path = [[NSBundle mainBundle] pathForResource:#"location" ofType:#"plist"];
NSArray *locations = [NSArray arrayWithContentsOfFile:path];
for (NSDictionary *row in locations){
NSNumber *latitude = [row objectForKey:#"latitude"];
NSNumber *longitude = [row objectForKey:#"longitude"];
NSString *title = [row objectForKey:#"title"];
CLLocationCoordinate2D coord;
coord.latitude = latitude.doubleValue;
coord.longitude = longitude.doubleValue;
MapViewAnnotation *annotation = [[MapViewAnnotation alloc] initWithTitle:title AndCoordinate:coord];
[annotations addObject: annotation];
}
return annotations;
}
#end
I am taking am going through a plist where I have coordinates for one test location that I am trying to show on my map, but the pin is not displaying on the map. Again, I am doing this all programmatically, no xib, or storyboards (although that should not be the problem).
I do not know why the annotation is not showing up. I tried looking on stack for some other situations like mine but was unlucky to find anything that could help.
Thank you.
Answer was pretty simple,
In my plist I had the coordinates
latitude: 42.37
longitude: 72.536
when it should of actually been -72.536 for longitude.
I´m trying to to add many Annotations (depends on how many objects a have in my Array) to my Mapview like this:
-(void)viewWillAppear:(BOOL)animated
{
[mapView removeAnnotations:mapView.annotations];
for (Daten *info in datenArray) {
CLLocationCoordinate2D location;
location.latitude = (double)[info.lati doubleValue];
location.longitude = (double)[info.longi doubleValue];
MapPin *newAnnotation =[[[MapPin alloc] initWithTitle:info.rating andCoordinate:location] autorelease];
[mapView addAnnotation:newAnnotation];
}
[self zoomToFitMapAnnotations:self.mapView];
[self.mapView selectAnnotation:self.mapView.annotations.lastObject animated:YES];
}
The first time I switch to that View it works perfectly...But if i come a second time to this View, i get this Bad Access with Error:
*** -[CFString length]: message sent to deallocated instance 0x17e140c0
Edit:
Thats my MapPin Class:
#interface MapPin : NSObject <MKAnnotation> {
NSString *title;
NSString *subtitle;
CLLocationCoordinate2D coordinate;
}
#property (nonatomic, copy) NSString *title;
#property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
#property (nonatomic, readonly, copy) NSString *subtitle;
- (id)initWithTitle:(NSString *)ttl andCoordinate:(CLLocationCoordinate2D)c2d;
EDIT2:
Edit3:
Here´s the implementation:
#import "MapPin.h"
#implementation MapPin
#synthesize title, coordinate, subtitle;
- (id)initWithTitle:(NSString *)ttl andCoordinate:(CLLocationCoordinate2D)c2d {
[super init];
subtitle = ttl;
title = #"Rating:";
coordinate = c2d;
return self;
}
- (void)dealloc {
[title release];
[subtitle release];
[super dealloc];
}
#end
You should send retain or copy message for object in init.
if (self = [super init]) {
subtitle = [ttl copy];
title = [#"Rating:" retain]
...
}
return self;
#"Rating:" is equal method which create autoreleased object.
Or you can use self.title = #"Rating:";.
I'm looking to adapt the following tutorial to create annotations from a plist. I'm brand new to Objective C and Xcode, and tried many different tutorials but can't seem to get this together.
Annotation.h
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface Annotation : NSObject <MKAnnotation>
#property (nonatomic, assign) CLLocationCoordinate2D coordinate;
#property (nonatomic, copy) NSString * title;
#property (nonatomic, copy) NSString * subtitle;
#property (nonatomic, copy) UIImageView * leftCalloutAccessoryView;
#end
Annotation.m
#import "Annotation.h"
#implementation Annotation
#synthesize coordinate, title, subtitle, leftCalloutAccessoryView;
#end
ViewController.h
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#interface ViewController : UIViewController
#property (weak, nonatomic) IBOutlet MKMapView *myMapView;
#end
ViewController.m
#import "ViewController.h"
#import "Annotation.h"
#interface ViewController ()
#end
//Wimbledon Coordinates
#define WIMB_LATITUDE 51.434783;
#define WIMB_LONGITUDE -0.213428;
//Stadium Coordinates
#define ARSENAL_LATITUDE 51.556899;
#define ARSENAL_LONGITUDE -0.106403;
#define CHELSEA_LATITUDE 51.481314;
#define CHELSEA_LONGITUDE -0.190129;
//Span
#define THE_SPAN 0.10f;
#implementation ViewController
#synthesize myMapView;
- (void)viewDidLoad
{
[super viewDidLoad];
// Create the region
MKCoordinateRegion myRegion;
//Center
CLLocationCoordinate2D center;
center.latitude = ARSENAL_LATITUDE;
center.longitude = ARSENAL_LONGITUDE;
//Span
MKCoordinateSpan span;
span.latitudeDelta = THE_SPAN;
span.longitudeDelta = THE_SPAN;
myRegion.center = center;
myRegion.span=span;
//Set our mapView
[myMapView setRegion:myRegion animated:YES];
//Annotation
NSMutableArray * locations = [[NSMutableArray alloc] init];
CLLocationCoordinate2D location;
Annotation * myAnn;
//Arsenal Annotation
myAnn = [[Annotation alloc] init];
location.latitude = ARSENAL_LATITUDE;
location.longitude = ARSENAL_LONGITUDE;
myAnn.coordinate = location;
myAnn.title = #"Arsenal FC";
myAnn.subtitle = #"The Gunners";
//myAnn.image = [UIImage imageNamed:#"location.png"];
[locations addObject:myAnn];
//Chelsea Annotation
myAnn = [[Annotation alloc] init];
location.latitude = CHELSEA_LATITUDE;
location.longitude = CHELSEA_LONGITUDE;
myAnn.coordinate = location;
myAnn.title = #"Chelsea FC";
myAnn.subtitle = #"The Blue Lions";
[locations addObject:myAnn];
[self.myMapView addAnnotations:locations];
}
//THIS CODE WORKS FOR CUSTOM ANNOTATIONS
- (MKAnnotationView *)mapView:(MKMapView *)map viewForAnnotation:(id <MKAnnotation>)annotation
{
MKPinAnnotationView *newAnnotation = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"redpin"];
newAnnotation.pinColor = MKPinAnnotationColorRed;
UIImageView *IconView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"marker.png"]];
newAnnotation.leftCalloutAccessoryView = IconView;
newAnnotation.animatesDrop = YES;
newAnnotation.canShowCallout = YES;
return newAnnotation;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Got it to work thanks to a suggestion above. Also added in custom markers and a left icon to call out. I commented out the old coordinate code and the annotation red pins with left icon.
Hope this helps others who might've gone crazy searching for a solution as I have.
Stadiums.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Stadiums</key>
<array>
<dict>
<key>Title</key>
<string>Arsenal</string>
<key>Subtitle</key>
<string>The Gunners</string>
<key>Latitude</key>
<string>51.556899</string>
<key>Longitude</key>
<string>-0.106403</string>
</dict>
<dict>
<key>Title</key>
<string>Chelsea</string>
<key>Subtitle</key>
<string>The Blue Lions</string>
<key>Latitude</key>
<string>51.481314</string>
<key>Longitude</key>
<string>-0.190129</string>
</dict>
</array>
</dict>
</plist>
ViewController.m
#import "ViewController.h"
#import "Annotation.h"
#interface ViewController ()
#end
//Wimbledon Coordinates
#define WIMB_LATITUDE 51.434783;
#define WIMB_LONGITUDE -0.213428;
//Stadium Coordinates
#define ARSENAL_LATITUDE 51.556899;
#define ARSENAL_LONGITUDE -0.106403;
#define CHELSEA_LATITUDE 51.481314;
#define CHELSEA_LONGITUDE -0.190129;
//Span
#define THE_SPAN 0.10f;
#implementation ViewController
#synthesize myMapView;
- (void)viewDidLoad
{
[super viewDidLoad];
// Create the region
MKCoordinateRegion myRegion;
//Center
CLLocationCoordinate2D center;
center.latitude = ARSENAL_LATITUDE;
center.longitude = ARSENAL_LONGITUDE;
//Span
MKCoordinateSpan span;
span.latitudeDelta = THE_SPAN;
span.longitudeDelta = THE_SPAN;
myRegion.center = center;
myRegion.span=span;
//Set our mapView
[myMapView setRegion:myRegion animated:YES];
/*
//Annotation
NSMutableArray * locations = [[NSMutableArray alloc] init];
CLLocationCoordinate2D location;
Annotation * myAnn;
//Arsenal Annotation
myAnn = [[Annotation alloc] init];
location.latitude = ARSENAL_LATITUDE;
location.longitude = ARSENAL_LONGITUDE;
myAnn.coordinate = location;
myAnn.title = #"Arsenal FC";
myAnn.subtitle = #"The Gunners";
//myAnn.image = [UIImage imageNamed:#"location.png"];
[locations addObject:myAnn];
//Chelsea Annotation
myAnn = [[Annotation alloc] init];
location.latitude = CHELSEA_LATITUDE;
location.longitude = CHELSEA_LONGITUDE;
myAnn.coordinate = location;
myAnn.title = #"Chelsea FC";
myAnn.subtitle = #"The Blue Lions";
[locations addObject:myAnn];
[self.myMapView addAnnotations:locations];
*/
NSMutableArray *annotations = [[NSMutableArray alloc]init];
NSString *path = [[NSBundle mainBundle] pathForResource:#"Stadiums" ofType:#"plist"];
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path];
NSArray *anns = [dict objectForKey:#"Stadiums"];
NSLog(#"read1");
for(int i = 0; i < [anns count]; i++) {
NSLog(#"read2");
float realLatitude = [[[anns objectAtIndex:i] objectForKey:#"Latitude"] floatValue];
float realLongitude = [[[anns objectAtIndex:i] objectForKey:#"Longitude"] floatValue];
NSLog(#"read3");
Annotation *myAnnotation = [[Annotation alloc] init];
CLLocationCoordinate2D theCoordinate;
theCoordinate.latitude = realLatitude;
theCoordinate.longitude = realLongitude;
myAnnotation.coordinate = theCoordinate;
myAnnotation.title = [[anns objectAtIndex:i] objectForKey:#"Title"];
myAnnotation.subtitle = [[anns objectAtIndex:i] objectForKey:#"Subtitle"];
[myMapView addAnnotation:myAnnotation];
[annotations addObject:myAnnotation];
//[myAnnotation release];
}
}
//THIS CODE WORKS FOR CUSTOM ANNOTATIONS
- (MKAnnotationView *)mapView:(MKMapView *)map viewForAnnotation:(id <MKAnnotation>)annotation
{
static NSString *AnnotationViewID = #"annotationViewID";
MKAnnotationView *annotationView = (MKAnnotationView *)[myMapView dequeueReusableAnnotationViewWithIdentifier:AnnotationViewID];
if (annotationView == nil)
{
annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:AnnotationViewID];
}
//Custom Pin
annotationView.image = [UIImage imageNamed:#"toilets.png"];
//Custom Thumbnail (left side)
UIImageView *IconView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"toilets.png"]];
annotationView.leftCalloutAccessoryView = IconView;
annotationView.canShowCallout = YES;
annotationView.annotation = annotation;
return annotationView;
/*
MKPinAnnotationView *newAnnotation = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"redpin"];
newAnnotation.pinColor = MKPinAnnotationColorRed;
UIImageView *ThumbView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"toilets.png"]];
newAnnotation.leftCalloutAccessoryView = ThumbView;
newAnnotation.animatesDrop = YES;
newAnnotation.canShowCallout = YES;
return newAnnotation;
*/
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Personally, I would use CoreData to store the annotations. However, if you are stuck on writing things to disk, I would make your annotation object implement the NSCoding protocol and use NSKeyedArchiver to save and load your objects.
+ (NSObject *)readArchiveFile:(NSString *)inFileName
{
NSFileManager *fileMgr = [NSFileManager defaultManager];
NSString *documentsDirectoryPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
NSString *filePath = [NSString stringWithFormat:#"%#/%#", documentsDirectoryPath, inFileName];
NSObject *returnObject = nil;
if( [fileMgr fileExistsAtPath:filePath] )
{
#try
{
returnObject = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
}
#catch (NSException *exception)
{
[FileUtils deleteFile:inFileName];
returnObject = nil;
}
}
return returnObject;
}
+ (void)archiveFile:(NSString *)inFileName inObject:(NSObject *)inObject
{
NSString *documentsDirectoryPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
NSString *filePath = [NSString stringWithFormat:#"%#/%#", documentsDirectoryPath, inFileName];
#try
{
BOOL didSucceed = [NSKeyedArchiver archiveRootObject:inObject toFile:filePath];
if( !didSucceed )
{
NSLog(#"File %# write operation %#", inFileName, didSucceed ? #"success" : #"error" );
}
}
#catch (NSException *exception)
{
NSLog(#"File %# write operation threw an exception:%#", filePath, exception.reason);
}
}
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?
I have a problem with centering the map to a particular region. I don't know where the problem is
Map.h
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#interface Map : UIViewController {
MKMapView * mapView;
}
#property(nonatomic,retain) IBOutlet MKMapView * mapView;
- (IBAction)setMap:(id)sender;
- (IBAction)getLocation:(id)sender;
#end
Map.m
#import "Map.h"
#import "Annotation.h"
#interface Map ()
#end
//define coordinates
#define Sofia_LATITUDE 42.745826;
#define Sofia_LONGITUDE 23.270186;
#define Plovdiv_LATITUDE 42.1333;
#define Plovdiv_LONGITUDE 24.75;
#define Varna_LATITUDE 43.2;
#define Varna_LONGITUDE 27.9167;
#define THE_SPAN 3.8;
#implementation Map
#synthesize mapView;
- (IBAction)getLocation:(id)sender {
mapView.showsUserLocation = YES;
[self.view addSubview:mapView];
}
- (IBAction)setMap:(id)sender {
switch (((UISegmentedControl *)sender).selectedSegmentIndex) {
case 0:
mapView.mapType = MKMapTypeStandard;
break;
case 1:
mapView.mapType = MKMapTypeSatellite;
break;
case 2:
mapView.mapType = MKMapTypeHybrid;
break;
default:
break;
}
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
MKCoordinateRegion myRegion;
//Center the map
CLLocationCoordinate2D center;
center.latitude = 42.1333;
center.longitude = 24.75;
MKCoordinateSpan span;
span.latitudeDelta = THE_SPAN;
span.longitudeDelta = THE_SPAN;
myRegion.center = center;
myRegion.span = span;
[mapView setRegion:myRegion animated:TRUE];
NSMutableArray * locations = [[NSMutableArray alloc]init];
CLLocationCoordinate2D location;
Annotation * myAnn;
//make annotations
myAnn = [[Annotation alloc]init];
location.latitude = Sofia_LATITUDE;
location.longitude = Sofia_LONGITUDE;
myAnn.coordinate = location;
myAnn.title = #"Милото";
myAnn.subtitle = #"Моята любов е ТУК!!!";
[locations addObject:myAnn];
myAnn = [[Annotation alloc]init];
location.latitude = Plovdiv_LATITUDE;
location.longitude = Plovdiv_LONGITUDE;
myAnn.coordinate = location;
myAnn.title = #"Plovdiv";
myAnn.subtitle = #"Plovdiv";
[locations addObject:myAnn];
myAnn = [[Annotation alloc]init];
location.latitude = Varna_LATITUDE;
location.longitude = Varna_LONGITUDE;
myAnn.coordinate = location;
myAnn.title = #"Varna";
myAnn.subtitle = #"Varna";
[locations addObject:myAnn];
[self.mapView addAnnotations:locations];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Annotation.h
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface Annotation : NSObject <MKAnnotation>
#property (nonatomic, assign) CLLocationCoordinate2D coordinate;
#property (nonatomic, copy) NSString * title;
#property (nonatomic, copy) NSString * subtitle;
#end
Annotation.m
#import "Annotation.h"
#implementation Annotation
#synthesize coordinate, title, subtitle;
#end
So when I launch the program and click the button to see the map, it appears but it is not centered. When I move back and again click to see the map everything is OK. Where is the problem?
You need to set the region for the mapview. The region has a coordinate (the middle) and a span (the numbers of the span stand for the degrees of the world that is shown on the map), play around with the span numbers to get the result you desire. You can set the region when you set your coordinate.
MKCoordinateRegion region;
MKCoordinateSpan span;
span.latitudeDelta = 0.2;
span.longitudeDelta = 0.2;
region.span = span;
region.center = self.coordinate;
[mapView setRegion:region animated:YES];