CLPlacemark - State Abbreviations? - iphone

I was wondering if it was possible to get the state abbreviations from CLPlacemark?
In the CLPlacemark Reference from Apple it states:
administrativeArea
The state or province associated with the placemark. (read-only)
#property(nonatomic, readonly) NSString *administrativeArea
Discussion
If the placemark location is Apple’s headquarters, for example, the value for this property would be the string “CA” or “California”.
but whenever I use it, I only get the full state (i.e California) and not the abbreviation (i.e CA). Can anyone help me here?

For anyone else that needs a solution for this, I've created a category class for CLPlacemark that returns the short state string. All you need to do is call myPlacemark shortState
CLPlacemark+ShortState.h
#import <CoreLocation/CoreLocation.h>
#import <Foundation/Foundation.h>
#interface CLPlacemark (ShortState)
- (NSString *)shortState;
#end
CLPlacemark+ShortState.m
#import "CLPlacemark+ShortState.h"
#interface CLPlacemark (ShortStatePrivate)
- (NSDictionary *)nameAbbreviations;
#end
#implementation CLPlacemark (ShortState)
- (NSString *)shortState {
NSString *state = [self.administrativeArea lowercaseString];
if (state.length==0)
return nil;
return [[self nameAbbreviations] objectForKey:state];
}
- (NSDictionary *)nameAbbreviations {
static NSDictionary *nameAbbreviations = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
nameAbbreviations = [NSDictionary dictionaryWithObjectsAndKeys:
#"AL",#"alabama",
#"AK",#"alaska",
#"AZ",#"arizona",
#"AR",#"arkansas",
#"CA",#"california",
#"CO",#"colorado",
#"CT",#"connecticut",
#"DE",#"delaware",
#"DC",#"district of columbia",
#"FL",#"florida",
#"GA",#"georgia",
#"HI",#"hawaii",
#"ID",#"idaho",
#"IL",#"illinois",
#"IN",#"indiana",
#"IA",#"iowa",
#"KS",#"kansas",
#"KY",#"kentucky",
#"LA",#"louisiana",
#"ME",#"maine",
#"MD",#"maryland",
#"MA",#"massachusetts",
#"MI",#"michigan",
#"MN",#"minnesota",
#"MS",#"mississippi",
#"MO",#"missouri",
#"MT",#"montana",
#"NE",#"nebraska",
#"NV",#"nevada",
#"NH",#"new hampshire",
#"NJ",#"new jersey",
#"NM",#"new mexico",
#"NY",#"new york",
#"NC",#"north carolina",
#"ND",#"north dakota",
#"OH",#"ohio",
#"OK",#"oklahoma",
#"OR",#"oregon",
#"PA",#"pennsylvania",
#"RI",#"rhode island",
#"SC",#"south carolina",
#"SD",#"south dakota",
#"TN",#"tennessee",
#"TX",#"texas",
#"UT",#"utah",
#"VT",#"vermont",
#"VA",#"virginia",
#"WA",#"washington",
#"WV",#"west virginia",
#"WI",#"wisconsin",
#"WY",#"wyoming",
nil];
});
return nameAbbreviations;
}
#end

I think you can't get the abbreviations of the states but you can make your own class for this..
List all the states(states are standards)
compare those states and return the abbreviation
Code..
Class StateAbbreviation
StateAbbreviation.h
#interface StateAbbreviation : NSString {
}
+ (NSString *)allStates:(int)index;
+ (NSString *)abbreviatedState:(NSString *)strState;
#end
StateAbbreviation.m
#implementation StateAbbreviation
+ (NSString *)allStates:(NSString *)strState {
// Remove all space on the string
strState = [strState stringByReplacingOccurrencesOfString:#" " withString:#""];
//Sample states
NSArray *states = [[NSArray alloc] initWithObjects:
#"ALABAMA",
#"ALASKA", //AK
#"AMERICANSAMOA", //AS
#"ARIZONA", //AZ
#"ARKANSAS", //AR
#"CALIFORNIA", //CA
nil];
NSUInteger n = [states indexOfObject:strState];
if (n > [states count] - 1) {
strAbbreviation = #"NOSTATE";
}
else {
strAbbreviation =[self abbreviatedState:n];
}
[states release];
return strAbbreviation;
}
+ (NSString *)abbreviatedState:(int)index {
NSArray *states = [[NSArray alloc] initWithObjects:
#"AL",
#"AK",
#"AS",
#"AZ",
#"AR",
#"CA",
nil];
NSString *strAbbreviation = [states objectAtIndex:index];
[states release];
return strAbbreviation;
}
#end
When you call the class it should be something like this
NSString *upperCase = [#"California" uppercaseString]; // California could be from (NSString *)placemark.administrativeArea;
NSString *abbr = [StateAbbreviation allStates:upperCase];
NSLog(#"%#", abbr); // Result should be CA
This are only samples you can research all states something like this, states and their abbreviations also like this states and their abbreviations

I believe the documentation is just incorrect. The administrativeArea is always going to return the full state name for places in the United States. To get the state abbreviation you'll most likely have to create a dictionary look up table so that searching for the key "California" will return you the value "CA".

Here is another category using FormattedAddressLines, it returns a result like California, CA
-(NSString *) stateWithAbbreviation {
if ([[self.addressDictionary objectForKey:#"CountryCode"] isEqualToString:#"US"] && self.addressDictionary) {
NSDictionary *addressLines = [self.addressDictionary objectForKey:#"FormattedAddressLines"];
for (NSString* addressLine in addressLines) {
NSRange stateRange = [addressLine rangeOfString:self.postalCode options:NSCaseInsensitiveSearch];
if (stateRange.length > 0) {
NSRange lastSpace = [addressLine rangeOfString:#" " options:NSBackwardsSearch];
if (lastSpace.length > 0) {
NSString *state = [[addressLine substringToIndex:lastSpace.location] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
lastSpace = [state rangeOfString:#" " options:NSBackwardsSearch];
if (lastSpace.length > 0) {
NSString *abbr = [[state substringFromIndex:lastSpace.location] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
return [NSString stringWithFormat:#"%#, %#", self.administrativeArea, abbr];
}
}
}
}
}
return self.administrativeArea;
}
Not perfect but it works as long as Apple changes the format of the address lines I think.

For people who need the state list with objects and keys swapped (e.g. on iOS 7 I get "CA" from placemark.administrativeArea):
NSDictionary *nameAbbreviations = [NSDictionary dictionaryWithObjectsAndKeys:
#"alabama",#"AL",
#"alaska",#"AK",
#"arizona",#"AZ",
#"arkansas",#"AR",
#"california",#"CA",
#"colorado",#"CO",
#"connecticut",#"CT",
#"delaware",#"DE",
#"district of columbia",#"DC",
#"florida",#"FL",
#"georgia",#"GA",
#"hawaii",#"HI",
#"idaho",#"ID",
#"illinois",#"IL",
#"indiana",#"IN",
#"iowa",#"IA",
#"kansas",#"KS",
#"kentucky",#"KY",
#"louisiana",#"LA",
#"maine",#"ME",
#"maryland",#"MD",
#"massachusetts",#"MA",
#"michigan",#"MI",
#"minnesota",#"MN",
#"mississippi",#"MS",
#"missouri",#"MO",
#"montana",#"MT",
#"nebraska",#"NE",
#"nevada",#"NV",
#"new hampshire",#"NH",
#"new jersey",#"NJ",
#"new mexico",#"NM",
#"new york",#"NY",
#"north carolina",#"NC",
#"north dakota",#"ND",
#"ohio",#"OH",
#"oklahoma",#"OK",
#"oregon",#"OR",
#"pennsylvania",#"PA",
#"rhode island",#"RI",
#"south carolina",#"SC",
#"south dakota",#"SD",
#"tennessee",#"TN",
#"texas",#"TX",
#"utah",#"UT",
#"vermont",#"VT",
#"virginia",#"VA",
#"washington",#"WA",
#"west virginia",#"WV",
#"wisconsin",#"WI",
#"wyoming",#"WY",
nil];

As of at least iOS 8, CLPlacemark's administrativeArea returns a two-letter abbreviation for US States.
You don't need to extend CLPlacemark with a category like the one in the accepted answer as long as you're targeting iOS 8 and newer (which you should be by now).
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder geocodeAddressString:#"1 Infinite Loop, Cupertino, CA" completionHandler:^(NSArray *placemarks, NSError *error) {
CLPlacemark *placemark = [placemarks firstObject];
NSLog(#"State: %#", placemark.administrativeArea);
}];
Run this and you'll get:
State: CA

SWIFT variant of dictionary
let states = [
"AL":"alabama",
"AK":"alaska",
"AZ":"arizona",
"AR":"arkansas",
"CA":"california",
"CO":"colorado",
"CT":"connecticut",
"DE":"delaware",
"DC":"district of columbia",
"FL":"florida",
"GA":"georgia",
"HI":"hawaii",
"ID":"idaho",
"IL":"illinois",
"IN":"indiana",
"IA":"iowa",
"KS":"kansas",
"KY":"kentucky",
"LA":"louisiana",
"ME":"maine",
"MD":"maryland",
"MA":"massachusetts",
"MI":"michigan",
"MN":"minnesota",
"MS":"mississippi",
"MO":"missouri",
"MT":"montana",
"NE":"nebraska",
"NV":"nevada",
"NH":"new hampshire",
"NJ":"new jersey",
"NM":"new mexico",
"NY":"new york",
"NC":"north carolina",
"ND":"north dakota",
"OH":"ohio",
"OK":"oklahoma",
"OR":"oregon",
"PA":"pennsylvania",
"RI":"rhode island",
"SC":"south carolina",
"SD":"south dakota",
"TN":"tennessee",
"TX":"texas",
"UT":"utah",
"VT":"vermont",
"VA":"virginia",
"WA":"washington",
"WV":"west virginia",
"WI":"wisconsin",
"WY":"wyoming"
]

Related

Sort array with a count value

I have an array which contains some strings. For each character of a string an integer value is assigned. For example a=2,b=5,c=6 ,o=1,k=3 etc
The final value in the a string is the sum of the character's value. So that for an example string "BOOK" the string will be stored as "BOOK (7)". Similarly every string will have a final integer value. I would like to sort these array with these final integer values stored in the string which is present in each array index. The array contains more than 200,000 words. So the sorting process should be pretty fast. Is there any method for it?
A brutal quick example could be, if your strings structure is always the same, like "Book (7)" you can operate on the string by finding the number between the "()" and then you can use a dictionary to store temporally the objects:
NSMutableArray *arr=[NSMutableArray arrayWithObjects:#"Book (99)",#"Pencil (66)",#"Trash (04)", nil];
NSLog(#"%#",arr);
NSMutableDictionary *dict=[NSMutableDictionary dictionary];
//Find the numbers and store each element in the dictionary
for (int i =0;i<arr.count;i++) {
NSString *s=[arr objectAtIndex:i];
int start=[s rangeOfString:#"("].location;
NSString *sub1=[s substringFromIndex:start];
NSString *temp1=[sub1 stringByReplacingOccurrencesOfString:#"(" withString:#""];
NSString *newIndex=[temp1 stringByReplacingOccurrencesOfString:#")" withString:#""];
//NSLog(#"%d",[newIndex intValue]);
[dict setValue:s forKey:newIndex];
}
//Sorting the keys and create the new array
NSArray *sortedValues = [[dict allKeys] sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
NSMutableArray *newArray=[[NSMutableArray alloc]init];
for(NSString *valor in sortedValues){
[newArray addObject:[dict valueForKey:valor]];
}
NSLog(#"%#",newArray);
This prints:
(
"Book (99)",
"Pencil (66)",
"Trash (04)"
)
(
"Trash (04)",
"Pencil (66)",
"Book (99)"
)
as i understand, you want to sort an array which contains string formated in the following
a=3
and you want to sort according to the number while ignoring the character.
in this case the following code will work with you
-(NSArray *)Sort:(NSArray*)myArray
{
return [myArray sortedArrayUsingComparator:(NSComparator)^(id obj1, id obj2)
{
NSString *first = [[obj1 componentsSeparatedByString:#"="] objectAtIndex:1];
NSString *second = [[obj2 componentsSeparatedByString:#"="] objectAtIndex:1];
return [first caseInsensitiveCompare:second];
}];
}
How to use it:
NSArray *arr= [[NSArray alloc] initWithObjects:#"a=3",#"b=1",#"c=4",#"f=2", nil];
NSArray *sorted = [self Sort:arr];
for (NSString* str in sorted)
{
NSLog(#"%#",str);
}
Output
b=1
f=2
a=3
c=4
Try this methods
+(NSString*)strTotalCount:(NSString*)str
{
NSInteger totalCount = 0;
// initial your character-count directory
NSDictionary* characterDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:2], [NSString stringWithUTF8String:"a"],
[NSNumber numberWithInt:5], [NSString stringWithUTF8String:"b"],
[NSNumber numberWithInt:6], [NSString stringWithUTF8String:"c"],
[NSNumber numberWithInt:1], [NSString stringWithUTF8String:"o"],
[NSNumber numberWithInt:3], [NSString stringWithUTF8String:"k"],
nil];
NSString* tempString = str;
for (NSInteger i =0; i<tempString.length; i++) {
NSString* character = [tempString substringWithRange:NSMakeRange(i, 1)];
character = [character lowercaseString];
NSNumber* count = [characterDictionary objectForKey:character];
totalCount += [count integerValue];
};
return [NSString stringWithFormat:#"%#(%d)",str,totalCount];
}
The test sentence:
NSLog(#"%#", [ViewController strTotalCount:#"BOOK"]);
will output " BOOK(10) "
You may change the ViewController to you own class name;
First - create a custom object to save your values. Don't put the value inside the string.
Sorting is not your base problem. The problem is that you are saving values into a string from where they are difficult to extract.
#interface StringWithValue
#property (nonatomic, copy, readwrite) NSString* text;
#property (nonatomic, assign, readwrite) NSUInteger value;
- (id)initWithText:(NSString*)text;
- (NSComparisonResult)compare:(StringWithValue*)anotherString;
#end
#implementation StringWithValue
#synthesize text = _text;
#synthesize value = _value;
- (id)initWithText:(NSString*)text {
self = [super init];
if (!self) {
return nil;
}
self.text = text;
self.value = [self calculateValueForText:text];
return self;
}
- (NSComparisonResult)compare:(StringWithValue*)anotherString {
if (self.value anotherString.value) {
return NSOrderedDescending;
}
else {
return NSOrderedSame;
}
}
- (NSString*)description {
return [NSString stringWithFormat:#"%# (%u)", self.text, self.value];
}
#end
Sorting the array then would be a simple use of sortUsingSelector:.
Note this will beat all other answers in performance as there is no need to parse the value with every comparison.

Array is remain still empty while fetching coordinates

I have array of country names and i want to get coordinates of them and store into another array. after this function my longitudes and latitudes arrays are remains still empty. do you have idea what is problem here ? i can't understand.
My Code is :
- (void)geocode {
NSString *strcountry=[[NSString alloc]init];
countryLati=[[NSMutableArray alloc]init];
countryLongi=[[NSMutableArray alloc]init];
countryName =[NSMutableArray arrayWithObjects:#"Bahrain",#"Bangladesh",#"Bhutan",#"China",#"Georgia",#"Hong Kong",nil ];
int count=[countryName count];
for(int i=0;i<count;i++)
{
strcountry=[countryName objectAtIndex:i];
[SVGeocoder geocode:strcountry
completion:^(NSArray *placemarks, NSHTTPURLResponse *urlResponse, NSError *error) {
//NSLog(#"placemarks = %#", placemarks);
CLPlacemark *place = [placemarks objectAtIndex:0];
CLLocation *location = place.location;
CLLocationCoordinate2D coord = location.coordinate;
NSString *tempLati=[[NSString alloc]initWithFormat:#"%g",coord.latitude];
NSString *tempLongi=[[NSString alloc]initWithFormat:#"%g",coord.longitude];
NSLog(#"-------------------------");
NSLog(#"Country : %#",strcountry);
NSLog(#"Latitude : %# ",tempLati);
NSLog(#"Longitude : %# ",tempLongi);
[countryLati addObject:tempLati];
[countryLongi addObject:tempLongi];
}];
}
NSLog(#"%d",[countryName count]);
NSLog(#"%d ",[countryLati count]);
NSLog(#"%d ",[countryLongi count]);
}
I accept Nuzhat Zari solution. Below is the code without using blocks and 3rd party Libraries. Its just a suggetion and showing you one more way of getting location details.
https://developers.google.com/maps/documentation/geocoding/
this URL has a good documentation for Google API.
- (void)geocode
{
NSString *strcountry=[[NSString alloc]init];
countryLati=[[NSMutableArray alloc]init];
countryLongi=[[NSMutableArray alloc]init];
countryName =[NSMutableArray arrayWithObjects:#"Bahrain",#"Bangladesh",#"Bhutan",#"China",#"Georgia",#"Hong Kong",nil ];
int count=[countryName count];
for(int i=0;i<count;i++)
{
strcountry=[countryName objectAtIndex:i];
NSString *urlString = [NSString stringWithFormat:#"http://maps.google.com/maps/geo?q=%#&output=csv", strCountry];
NSString* webStringURL = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *url = [NSURL URLWithString:webStringURL];
NSString *locationString = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil];
NSLog(#"Location String = %#", locationString);
NSArray *receivedGoogleData = [[NSArray alloc] initWithArray:[locationString componentsSeparatedByString:#","]];
searchTermLocation = [[CLLocation alloc] initWithLatitude:[[receivedGoogleData objectAtIndex:2] doubleValue] longitude:[[receivedGoogleData objectAtIndex:3] doubleValue]];
[countryLati addObject:[[receivedGoogleData objectAtIndex:2] doubleValue]];
[countryLongi addObject:[[receivedGoogleData objectAtIndex:3] doubleValue]];
}
NSLog(#"%d",[countryName count]);
NSLog(#"%d ",[countryLati count]);
NSLog(#"%d ",[countryLongi count]);
}
#Mahesh Dhapa : Try this one, From my perspective, I feel this is easy than your way of SVGeoCoder, because no need to extra importing of SVGeocoder classes. BTW, SVGeocoder is simple. But, This is also straight forward without using 3rd party libraries.
You get response from Google like this
200,1,23.8954,34.5478
In which,
200 - Success
1 - Accuracy
23.8954 - Latitude of location
34.5478 - Longitude of location
You can also get this in JSON and XML Format with extra clear information, But you need to decode them to Dictionary from JSON and XML format to use them.
Example JSON Format :
{
"name": "Bahrain",
"Status": {
"code": 200,
"request": "geocode"
},
"Placemark": [ {
"id": "p1",
"address": "Bahrain",
"AddressDetails": {
"Accuracy" : 1,
"Country" : {
"CountryName" : "Bahrain",
"CountryNameCode" : "BH"
}
},
"ExtendedData": {
"LatLonBox": {
"north": 26.3240565,
"south": 25.5798401,
"east": 50.8228639,
"west": 50.3781509
}
},
"Point": {
"coordinates": [ 50.5577000, 26.0667000, 0 ]
}
} ]
}
Example XML Response :
<kml xmlns="http://earth.google.com/kml/2.0">
<Response>
<name>Bahrain</name>
<Status>
<code>200</code>
<request>geocode</request>
</Status>
<Placemark id="p1">
<address>Bahrain</address>
<AddressDetails xmlns="urn:oasis:names:tc:ciq:xsdschema:xAL:2.0" Accuracy="1">
<Country>
<CountryNameCode>BH</CountryNameCode>
<CountryName>Bahrain</CountryName>
</Country>
</AddressDetails>
<ExtendedData>
<LatLonBox north="26.3240565" south="25.5798401" east="50.8228639" west="50.3781509"/>
</ExtendedData>
<Point>
<coordinates>50.5577000,26.0667000,0</coordinates>
</Point>
</Placemark>
</Response>
</kml>
I think when you want to change local variable in block you need to declare it as block, as follows:
NSArray *stringsArray = #[ #"string 1",
#"String 21",
#"string 12",
#"String 11",
#"Strîng 21",
#"Striñg 21",
#"String 02" ];
NSLocale *currentLocale = [NSLocale currentLocale];
__block NSUInteger orderedSameCount = 0;
NSArray *diacriticInsensitiveSortArray = [stringsArray sortedArrayUsingComparator:^(id string1, id string2) {
NSRange string1Range = NSMakeRange(0, [string1 length]);
NSComparisonResult comparisonResult = [string1 compare:string2 options:NSDiacriticInsensitiveSearch range:string1Range locale:currentLocale];
if (comparisonResult == NSOrderedSame) {
orderedSameCount++;
}
return comparisonResult;
}];
NSLog(#"diacriticInsensitiveSortArray: %#", diacriticInsensitiveSortArray);
NSLog(#"orderedSameCount: %d", orderedSameCount);
Edit:A statement from reference.
A powerful feature of blocks is that they can modify variables in the
same lexical scope. You signal that a block can modify a variable
using the __block storage type modifier.
So you should declare countryLati and countryLongi as block storage.

"Name" of a CLPlacemark

I'm trying to understand CLPlacemark and when/how to create information for a callout of a pin that is added to a map. Before what I read in More iOS 3 development a few years ago, they reverse geocoded an address and built the address (street, zip, state, etc). First, do I need to build this string myself? I was trying to find out how to get the name of a location for certain known things like searching for the apple store in the code below:
NSString *address = #"1 stockton, san francisco, ca";
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder geocodeAddressString:address completionHandler:^(NSArray *placemarks, NSError *error) {
[placemarks enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSLog(#"obj description: %#", [obj description]);
CLPlacemark *aPlacemark = (CLPlacemark *)obj;
NSLog(#"%#", [aPlacemark.addressDictionary description]);
NSLog(#"name: %#", ((CLPlacemark *)obj).name);
}];
];
When I print out the description, I see that the Console says:
Apple Store, San Francisco, 1 Stockton St, San Francisco, CA 94108-5805, United States # <+37.78584545,-122.40651752> +/- 100.00m, region (identifier <+37.78584545,-122.40652161> radius 18.96) <+37.78584545,-122.40652161> radius 18.96m
Where does it get the Apple Store, San Francisco, name? I thought it would be the CLPlacemark.name property, but that is null. So in trying to figure out how the name property is created, I found:
NSLog(#"%#", [aPlacemark.addressDictionary description]);
I get the output:
City = "San Francisco";
Country = "United States";
CountryCode = US;
FormattedAddressLines = (
"Apple Store, San Francisco",
"1 Stockton St",
"San Francisco, CA 94108-5805",
"United States"
);
PostCodeExtension = 5805;
State = California;
Street = "1 Stockton St";
SubAdministrativeArea = "San Francisco";
SubLocality = "Union Square";
SubThoroughfare = 1;
Thoroughfare = "Stockton St";
ZIP = 94108;
From this, all I can see is that in the FormattedAddressLines key of the addressDictionary, the title is there as well.
So I guess my 2 questions are:
1) How do I get the name of a location if there is one (i.e. Apple Store)?
2) Do I need to build my string anymore since it seems like the address dictionary does that for me already?
Thanks!
You can use ABCreateStringWithAddressDictionary function from AddressBookUI framework to get the address string from CLPlacemark's "addressDictionary" property.
To answer your first question, use the areasOfInterest property of the CLPlacemark.
As for the second question, thats really up to you, and how you want to format the string. If you want to build it using strings from the addressDictionary property, then by all means do so. You can pick and choose the parts, and create a string all in the completion handler.
Also, you mentioned you want to create an annotation for displaying on a map. I personally subclass MKAnnotation, and use that to quickly create an annotation to display on a map.
MyAnnotation.h
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface MyAnnotation : NSObject <MKAnnotation> {
NSString *_name;
NSString *_address;
CLLocationCoordinate2D _coordinate;
}
#property (copy) NSString *name;
#property (copy) NSString *address;
#property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
- (id)initWithName:(NSString*)name address:(NSString*)address coordinate:(CLLocationCoordinate2D)coordinate;
#end
MyAnnotation.m
#import "MyAnnotation.h"
#implementation MyAnnotation
#synthesize name = _name;
#synthesize address = _address;
#synthesize coordinate = _coordinate;
- (id)initWithName:(NSString*)name address:(NSString*)address coordinate:(CLLocationCoordinate2D)coordinate
{
if ((self = [super init])) {
_name = [name copy];
_address = [address copy];
_coordinate = coordinate;
}
return self;
}
- (NSString *)title
{
return _name;
}
- (NSString *)subtitle
{
return _address;
}
#end
Declare an NSMutableArray to hold all the annotations, and throw the initWithName:address:coordinate: in your completion handler, and you can quickly get an array of annotations you can add to your map.
NSString *address = #"1 stockton, san francisco, ca";
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder geocodeAddressString:address completionHandler:^(NSArray *placemarks, NSError *error)
{
NSMutableArray *annotationArray = [NSMutableArray array];
for (CLPlacemark *aPlacemark in placemarks)
{
NSString *nameString = // Get your name from an array created by aPlacemark.areasOfInterest;
NSString *addressString = // Put your address string info you get from the placemark here.
CLLocationCoordinate2D coordinate = aPlacemark.location.coordinate;
MyAnnotation *annotation = [[MyAnnotation alloc] initWithName:nameString address:addressString coordinate:coordinate];
[annotationArray addObject:annotation];
}
// Now save annotationArray if you want to use it later
// Add it to your MapView with [myMapView addAnnotations:annotationArray];
}];
Hope that helps out!

How do i remove a substring from an nsstring?

Ok, say I have the string "hello my name is donald"
Now, I want to remove everything from "hello" to "is"
The thing is, "my name" could be anything, it could also be "his son"
So basically, simply doing stringByReplacingOccurrencesOfString won't work.
(I do have RegexLite)
How would I do this?
Use like below it will help you
NSString *hello = #"his is name is isName";
NSRange rangeSpace = [hello rangeOfString:#" "
options:NSBackwardsSearch];
NSRange isRange = [hello rangeOfString:#"is"
options:NSBackwardsSearch
range:NSMakeRange(0, rangeSpace.location)];
NSString *finalResult = [NSString stringWithFormat:#"%# %#",[hello substringToIndex:[hello rangeOfString:#" "].location],[hello substringFromIndex:isRange.location]];
NSLog(#"finalResult----%#",finalResult);
The following NSString Category may help you. It works good for me but not created by me. Thanks for the author.
NSString+Whitespace.h
#import <Foundation/Foundation.h>
#interface NSString (Whitespace)
- (NSString *)stringByCompressingWhitespaceTo:(NSString *)seperator;
#end
NSString+Whitespace.m
#
import "NSString+Whitespace.h"
#implementation NSString (Whitespace)
- (NSString *)stringByCompressingWhitespaceTo:(NSString *)seperator
{
//NSArray *comps = [self componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
NSArray *comps = [self componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSMutableArray *nonemptyComps = [[NSMutableArray alloc] init];
// only copy non-empty entries
for (NSString *oneComp in comps)
{
if (![oneComp isEqualToString:#""])
{
[nonemptyComps addObject:oneComp];
}
}
return [nonemptyComps componentsJoinedByString:seperator]; // already marked as autoreleased
}
#end
If you always know your string will begin with 'hello my name is ', then that is 17 characters, including the final space, so if you
NSString * hello = "hello my name is Donald Trump";
NSString * finalNameOnly = [hello substringFromIndex:17];

Advice on High Score persistence (iPhone, Cocoa Touch)

I was curious what is considered the better way to manage the reading and writing of a High Score plist file. My High Score class is:
#interface HighScore : NSObject <NSCoding> {
NSString *name;
int score;
int level;
int round;
NSDate *date;
}
Now, I could do method A, add NSCoding methods:
- (void) encodeWithCoder: (NSCoder *) coder {
[coder encodeObject: name
forKey: kHighScoreNameKey];
[coder encodeInt: score
forKey: kHighScoreScoreKey];
[coder encodeInt: level
forKey: kHighScoreLevelKey];
[coder encodeInt: round
forKey: kHighScoreRoundKey];
[coder encodeObject: date
forKey: kHighScoreDateKey];
} // encodeWithCoder
- (id) initWithCoder: (NSCoder *) decoder {
if (self = [super init]) {
self.name = [decoder decodeObjectForKey: kHighScoreNameKey];
self.score = [decoder decodeIntForKey: kHighScoreScoreKey];
self.level = [decoder decodeIntForKey: kHighScoreLevelKey];
self.round = [decoder decodeIntForKey: kHighScoreRoundKey];
self.date = [decoder decodeObjectForKey: kHighScoreDateKey];
}
return (self);
} // initWithCoder
And write it all out with:
if (![NSKeyedArchiver archiveRootObject:highScoresList toFile:path]) ...
Reading it back in would be pretty straight forward. However the plist file, IMHO, looks like crap.
Or I could employ method B:
NSMutableArray *array = [NSMutableArray arrayWithCapacity:20];;
for (HighScore *hs in highScoresList) {
NSDictionary *dict = [[NSDictionary alloc] initWithObjectsAndKeys:
hs.name, kHighScoreNameKey,
[NSNumber numberWithInteger:hs.score], kHighScoreScoreKey,
[NSNumber numberWithInteger:hs.level], kHighScoreLevelKey,
[NSNumber numberWithInteger:hs.round], kHighScoreRoundKey,
hs.date, kHighScoreDateKey,
nil];
[array addObject:dict];
[dict release];
}
and write it all out with:
if (![array writeToFile:path atomically:YES]) ...
Reading it back in is a tiny bit harder. But the plist file looks much cleaner (smaller and compact).
Any thoughts? Am I missing something that is much simpler? (I want to keep the High Scores separate from NSUserDefaults so I am not using that).
Both your ways look fine to me. There is also Core Data in the 3.0 OS, although it might be overkill if all you want to save is a single high score value.
I'm not sure that I understand your objections! Both should work just fine.
Personally I prefer method A. I think it makes sense that an object knows how to encode itself. It makes maintenance easier and any changes more localised. Plus it probably uses less memory.
check out this question, maybe it's useful for your app
I went with method B because: 1. The plist file is more readable and 2. I can save off some file and class version numbering into this method easily:
In my HighScore class:
- (id)initFromDictionary: (NSDictionary *)dict;
{
if (self = [super init]) {
self.name = [dict objectForKey:kHighScoreNameKey];
self.score = [[dict objectForKey:kHighScoreScoreKey] integerValue];
self.game = [[dict objectForKey:kHighScoreGameKey] integerValue];
self.level = [[dict objectForKey:kHighScoreLevelKey] integerValue];
self.date = [dict objectForKey:kHighScoreDateKey];
}
return (self);
}
- (NSDictionary *)putIntoDictionary;
{
NSDictionary *dict = [[NSDictionary alloc] initWithObjectsAndKeys:
name, kHighScoreNameKey,
[NSNumber numberWithInt:score], kHighScoreScoreKey,
[NSNumber numberWithInt:game], kHighScoreGameKey,
[NSNumber numberWithInt:level], kHighScoreLevelKey,
date, kHighScoreDateKey,
nil];
return dict;
}
And in my HighScoreTable class:
- (id) load
{
NSString *path = [self getFilePath];
// [self clear];
NSDictionary *rootLevelPlistDict = [NSDictionary dictionaryWithContentsOfFile:path];
int versNum = [[rootLevelPlistDict objectForKey:kHighScoreVersKey] integerValue];
if (versNum == kHighScoreVersNum) {
NSArray *insideArray = [rootLevelPlistDict objectForKey:kHighScoresKey];
NSDictionary *dict;
for (dict in insideArray) {
HighScore *hs = [[HighScore alloc] initFromDictionary:dict];
[highScoresList addObject:hs];
[hs release];
}
}
return sharedHighScoresSingleton;
}
- (void) save
{
if (!changed)
return;
NSString *path = [self getFilePath];
NSMutableArray *array = [NSMutableArray arrayWithCapacity:kNumberOfHighScores];
for (HighScore *hs in highScoresList) {
NSDictionary *dict = [hs putIntoDictionary];
[array addObject:dict];
[dict release];
}
NSDictionary *rootLevelPlistDict = [[NSDictionary alloc] initWithObjectsAndKeys:
[[[NSBundle mainBundle] infoDictionary]
objectForKey:(NSString*)kCFBundleNameKey], kHighScoreAppNameKey,
[NSNumber numberWithInt:kHighScoreHeaderVersNum], kHighScoreHeaderVersKey,
[NSNumber numberWithInt:kHighScoreVersNum], kHighScoreVersKey,
[NSDate date], kHighScoreCreationKey,
array, kHighScoresKey,
nil];
if (![rootLevelPlistDict writeToFile:path atomically:YES])
NSLog(#"not successful in writing the high scores");
[rootLevelPlistDict release];
}