In the documentation for MKReverseGeocoder, the administrativeArea property gives you the current state the user is in, and it mentions in an example that it returns EITHER the state name OR its abbreviation. I am wondering if anyone knows how to get the abbreviation instead of the full state name...I have been able to find nothing that shows this is even a possibility besides that brief example that doesn't mention HOW.
Thanks!
I also needed to convert the State field from MKReverseGeocoder into a two letter abbreviation, so I created this plist:
https://github.com/djibouti33/US-State-Abbreviations
Here's how I use it:
// in my init
NSString *plistPath = [[NSBundle mainBundle] pathForResource:#"USStateAbbreviations" ofType:#"plist"];
self.usStateAbbreviations = [[NSDictionary alloc] initWithContentsOfFile:plistPath];
// MKReverseGeocoder delegate method
- (void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFindPlacemark:(MKPlacemark *)placemark {
...
NSString *state = [address objectForKey:#"State"];
NSString *stateAbbreviation = [self.usStateAbbreviations objectForKey:[state uppercaseString]];
NSString *stateTarget = state;
if (stateAbbreviation) {
stateTarget = stateAbbreviation;
}
...
}
There is no way to do this. Not sure why the Apple docs say "CA or California".
It's easy to convert state to 2 letter name. Just create a plist (table, or NSDictionary works too) of the following: http://www.usps.com/ncsc/lookups/usps_abbreviations.html and use that to look up the 2 letter abbreviations.
Related
//COPY THIS CODE IN A FRESH PROJECT!!!
//THIS 2 LINES ARE JUST EXAMPLES, OF VALUES PUSHES OUT A DATABASE
NSString *messagelevel1 = #"45";
NSString *currentlevel = #"1";
NSString *HuidigLevel = currentlevel;
NSDecimalNumber *huidigleveldec = [[NSDecimalNumber alloc] initWithString: HuidigLevel];
float HuidigLevelRek = [huidigleveldec floatValue];
//HERE IS THE PROBLEM
NSString* LevelTotaal=[[NSString alloc] initWithFormat:#"messagelevel%.f",HuidigLevelRek];
NSString*result = LevelTotaal;
NSLog(#"%#",result);
// THE ABOVE RESULT SHOULD RETURN THE SAME VALUE AS THE NEXT (messagelevel1) LINE BUT IT RETURNS ONLY "messagelevel1" AND NOT THE VALUE!
NSLog(#"%#",messagelevel1);
I want the *result string behaves like the *huidiglevel string and fetch some information, but because the LevelTotaal is a NSString, It doesn't fetch this information. I really got no idea where to google for this problem, searching the Developer docs didn't helped either . Maybe you guys can help me out?
Actually the second NSLog returns the value and to first NSLog just returns messagelevel1. To tell you in short ;)
I hope you guys get what I'm saying!
I think what you're trying to do is use variable variables, a system that does not exist in Objective-C. In PHP, you can use variable variables:
$hello = 'abcdef';
$varName = 'hello';
print $$varName; // prints the value of $hello, which is 'abcdef'
Like many things in PHP, this is not really a good way to design software. Instead, consider using something like a NSDictionary, this allows you to give specific data a key.
NSMutableDictionary *aDict = [NSMutableDictionary dictionary];
[aDict setObject:[NSNumber numberWithFloat:4.5] forKey:#"messageLevel1"];
NSString *result = [aDict objectForKey:#"messageLevel1"];
You can obtain the data dynamically, the key can be generated or obtained at runtime.
Edit:
Rather than having variables called messageLevel1, messageLevel2, messageLevel3 ... messageLeveln, just use an array.
NSMutableArray *messageLevels = [NSMutableArray array];
[messageLevels addObject:#"1"];
[messageLevels addObject:#"45"];
[messageLevels addObject:#"123"];
NSString *result = [messageLevels objectAtIndex:HuidigLevelRek];
I’m making a languages application, and I have a long list of vocabulary relating to that language (German, in case anyone was interested). I have the functionality in my app to switch between sorting the tableview by German words, or by english words.
When I use the following:
NSString *path = [[NSBundle mainBundle] pathForResource:name ofType:type];
NSString *string = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
NSArray *array = [[string componentsSeparatedByString:#"\n"] sortedArrayUsingSelector:#selector(compare:)];
it works absolutely perfectly - by which I mean, exactly as expected. What I would like to improve on this however, is that there are certain words, such as verbs or nouns, which are always preceded by prefixes, like “to”, as in “to do something”, or “the” in front of nouns. So what I would like to do is somehow exclude these from my sort, because otherwise I end up with all the verbs being sorted alphabetically under the “t” section in my array, which is not very user friendly.
I’ve looked through the Apple documentation about NSString and NSArray, as this is where the compare function is (unless I’m very much mistaken), and I haven’t found any way that makes sense to me. This is the first time I have done any data handling like this so I may be missing something simple, and so I would really appreciate some help.
Thanks very much
Michaeljvdw
You're on the right track. What you want to use instead of the (built-in) compare method is to write your own method, which can eliminate the "to" or "the" bits if they exist, and then use the existing compare method.
Your call would look something like this:
NSArray *array = [[string componentsSeparatedByString:#"\n"] sortedArrayUsingSelector:#selector(myCompare:)];
Using a custom category you give to NSString with the following methods:
// This method can be exposed in a header
- (NSComparisonResult)myCompare:(NSString*)aString
{
NSString* selfTrimmed = [self removeArticles];
NSString* aStringTrimmed = [s2 removeArticles];
return [self compare:aString];
}
// This method can be kept private in the .m implementation
- (NSString*)removeArticles
{
NSRange range = NSMakeRange(NSNotFound, 0);
if ([self hasPrefix:#"to "])
{
range = [self rangeOfString:#"to "];
}
else if ([self hasPrefix:#"the "])
{
range = [self rangeOfString:#"the "];
}
if (range.location != NSNotFound)
{
return [self substringFromIndex:range.length];
}
else
{
return self;
}
}
You might have some luck with localizedCompare: or localizedStandardCompare:, but I don't think that either of these will strip out articles and prepositions like you want. Instead, you will probably have to define a category on NSString that provides the specific style of sorting you're looking for:
#interface NSString (MySortAdditions)
- (NSComparisonResult)compareWithoutArticles:(NSString *)other;
#end
#implementation NSString (MySortAdditions)
- (NSComparisonResult)compareWithoutArticles:(NSString *)other {
NSMutableString *mutableSelf = [NSMutableString stringWithString:self];
[mutableSelf
replaceOccurrencesOfString:#"das"
withString:#""
options:NSCaseInsensitiveSearch
range:NSMakeRange(0, [mutableSelf length])
];
...
// delete articles from 'other' too
NSCharacterSet *trimSet = [NSCharacterSet whitespaceAndNewlineCharacterSet];
NSString *trimmedSelf = [mutableSelf stringByTrimmingCharactersInSet:trimSet];
NSString *trimmedOther = ...;
return [trimmedSelf localizedCaseInsensitiveCompare:trimmedOther];
}
#end
You can then use #selector(compareWithoutArticles:) as your sort selector for NSArray.
First, don't use compare:. Use localizedCompare: instead. This is important, because whether á appears just after a or after z as a separate letter depends on the language. localizedCompare: takes care of that.
--edit
As Justin says, localizedStandardCompare: is the selector to be used! I didn't know that method. As written in the documentation, localizedStandardCompare: does more than localizedCompare:, although the document doesn't say exactly what it does.
--end of edit
If you want more, you need to implement that yourself. You can use category for that purpose. First declare it
#interface NSString (MichaelsSuperCompareCategory)
-(NSComparisonResult)michaelsSuperCompare:(NSString*)string;
#end
and then implement it
#interface NSString (MichaelsSuperCompareCategory)
-(NSComparisonResult)michaelsSuperCompare:(NSString*)string{
...
}
#end
This way you can add methods to an existing class. Then you can use
NSArray *array = [[string componentsSeparatedByString:#"\n"]
sortedArrayUsingSelector:#selector(michaelsSuperCompare:)];
It is important to prefix the method name with something distinctive, not to accidentally crash with internal methods used by Apple.
As for the functionality, you need to implement that yourself, as far as I know. You can get the current locale with [NSLocale currentLocale]. You can implement a nicer behavior for the languages you know, and then default to localizedCompare: for unknown languages.
I would somehow do -replaceOccurancesOfStrings on all the data eg "To" -> "" - and then reload the data. (or this can in a text editor)
Another thing to think about is having eg 'to walk' changed to 'walk (to)' which can be done ahead of time (and will also create less confusion for the user as they are scrolling alphabetically).
NSDictionary *story = [stories objectAtIndex: indexPath.row];
cell.text=[NSString stringwithFormat:[story objectForKey#"message];
i dont knw what exaclty "message " contains (what is the meaning of objectForKey#"message")
EDIT CODE
NSString *key =[appDelegate.books objectAtIndex:indexPath.row];
//dict y=#"Name";
NSArray *nameSection = [dict objectForKey:key];
NSDictionary *story = [nameSection objectAtIndex: indexPath.row];
cell.text=[NSString stringwithFormat:[story objectForKey:key]];
NSLog(#"Value Of message: %#", [dict objectForKey:key]);
why my code crashes
If you are more familiar with Java or C# the code is equivalent to something like this:
// Assuming stories is declared as: List<Dictionary<string, string> stories;
Dictionary<string, string> story = stories[indexPath.row];
cell.Text = String.Format(story["message"]);
In Smalltalk-style (and therefore Objective-C too) Object Oriented programming, methods are more like messages to other objects. So a good Objective-C method name should read like an English sentence (Subject-Verb-Object). Because of this working with dictionaries (hash tables) looks like this:
[myDictionary setObject:#"Value" forKey:#"someKey"];
[myDictionary objectForKey:#"someKey"]; // == #"Value"
In Java it would be:
myDictionary.put("someKey", "Value");
myDictionary.get("someKey"); // == "Value"
Notice how the key ("someKey") was the first argument in the Java example. In Objective-C you name your arguments with the method name, hence setObject: forKey:. Also notice that in Objective-C strings start with an # symbol. That's because Objective-C strings are different from regular C strings. When using Objective-C you almost always use Objective-C's # strings.
In C# there is a special syntax for Dictionaries so it becomes:
myDictionary["someKey"] = "Value";
myDictionary["someKey"]; // == "Value"
One important problem that you might encounter if you're new is the problem of native types.
In Java to add an int to a Dictionary you used to have to do:
myDictionary.put("someKey", new Integer(10));
Because the primitive types (int, char/short, byte, boolean) aren't real Objects. Objective-C has this problem too. So if you want to put an int into a dictionary you must use NSNumber like so:
[myDictionary setObject:[NSNumber numberForInt:10]
forKey:#"someKey"];
And you pull out the integer like so:
NSNumber *number = [myDictionary objectForKey:#"someKey"];
[number intValue]; // == 10
EDIT:
Your code might be crashing if you have a '%' character in your string, since stringWithFormat is just like NSLog in that it takes many arguments. So if story["message"] is "Hello" then it'll work fine without extra arguments but if it's "Hello %#" you need to add one argument to stringWithFormat.
NSString *message = #"Hello %#";
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setObject:message forKey:#"message"];
NSString *output = [NSString stringWithFormat:[dict objectForKey:#"message"], #"World!"];
// output is now #"Hello World!".
#"message" is a key for a value stored in the NSDictionary object. The first line declares an NSDictionary named story that appears to come from an array.
If you want to find what value is stored for the key:#"message", consider using:
NSLog(#"Value Of message: %#", cell.text);
Run and check the console to see the output. (SHIFT + COMMAND + Y) in XCode will bring up the console, if that's what you are using. If you are unfamiliar with NSArrays/NSDictionaries, give Apple's Documentation a look.
I'm just guessing at all of this since that is a very limited sample of code. Try submit more code when you ask a question so that the viewers can get a better idea of your questions.
That is an example of key-value coding, and a lot of information is available on the Apple dev site if you're interested:
http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/KeyValueCoding/KeyValueCoding.html
I have been trying to get this to work for about three solid days and get my mind around it. Can someone advise.
I have built the basics and they all work great but when I try to do that extra bit I cannot get my head around it.
I am trying to build a table of technical terms, from a plist. This is an indexed and sections table by the alphabet.
This works fine but when I then try to add the next level for each term's definition in a new viewcontroller I can't seem to get the code or the plist structure right.
At the moment I have created two plists. One with a dictionary of the alphabet in 26 arrays, within each array is a series of technical terms. All this works great.
Then I've created another plist of definitions as an array of dictionaries, one for each word/definition pair. I'm expecting to be passing the #"word" key from the view controller to the detailviewcontroller then picking up the #"definition". I don't know whether this is right or wrong(?)
My code shows the technical term table great but when a row is selected it crashes. I know it's to do with the code for passing the detailviewcontroller's reference so the detailview can pick up the definition - but I've no idea how to solve it. I've posted parts of my code here for someone to look at help. Any ideas?
NSString *wordPath = [[NSBundle mainBundle] pathForResource:#"newsortedglossary" ofType:#"plist"];
NSDictionary *wordDict = [[NSDictionary alloc] initWithContentsOfFile:wordPath];
self.words = wordDict;
[words release];
NSArray *wordArray = [[words allKeys] sortedArrayUsingSelector:#selector(compare:)];
self.wordKeys = wordArray;
NSString *definitionPath = [[NSBundle mainBundle] pathForResource:#"newnewdefinitionglossary" ofType:#"plist"];
NSDictionary *definitionDict = [[NSDictionary alloc] initWithContentsOfFile:definitionPath];
self.definitions = definitionDict;
[definitions release];
didSelectRow here.........
GlossaryDetailViewController *glossaryDetailViewController = [[GlossaryDetailViewController alloc]
initWithNibName:#"GlossaryDetailView" bundle:nil];
NSLog(#"did select-2"); // CRASHES HERE with NSDictionary may not respond to objectAtIndex
glossaryDetailViewController.definition = [self.words objectAtIndex:indexPath.row];
NSLog(#"did select-3");
[self.navigationController pushViewController:glossaryDetailViewController animated:YES];
NSLog(#"did select-4");
[glossaryDetailViewController release];
detailViewController here.......
- (void)viewDidAppear:(BOOL)animated {
NSLog(#"didappear");
self.glossaryWordDefinition.text = [definition objectForKey:#"definition"];
It seems that you are trying to access the members of dictionary by using an index, instead of using a key to lookup the associated value.
In your didSelectRow you probably want this:
glossaryDetailViewController.definition = [self.wordKeys objectAtIndex:indexPath.row];
The difference is that now you are trying access the members of an array with the index.
How can I make my application display something only when they first launch the application for the first time. Example: They open up my app, an alert comes up, saying something like, "Do you want to play the tutorial?" Then, if they close the app, then re-open it, it won't show up again.
Thanks
I'd recommend using NSUserDefaults:
- (void)openOneTime
{
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
static const NSString* kKey = #"One Time Key";
NSObject* keyValue = [defaults objectForKey:kKey];
if (keyValue == nil)
{
[self doMyOneTimeThing]; // pop a dialog, etc...
}
// Adds an object for our key which will get found the next time around,
// bypassing the above code block. The type and value of the object is
// not important; what matters more is that an object exists for that
// key at all.
[defaults setBool:YES forKey:kKey];
}
More tips on storing data persistently:
Method 1: Use the global user preferences system. You can do this, but it might be considered slightly hacky because it is designed to store user preferences, and I think this is a gray area, since the user doesn't have explicit control here. In any case, check out the docs for NSUserDefaults to find out how to do that.
Method 2: Write to a file whose existence indicates whether or not the tutorial has been viewed. You can easily create a file with an NSData object by calling its writeToFile:atomically: method. Later, you can use the NSFileManager class to check if that file exists or not.
Sample code:
- (NSString*) filename {
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString* documentsDirectory = [paths objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:#"notFirstTime"];
}
- (void) setNotFirstTime {
NSData* data = [[[NSData alloc] init] autorelease];
[data writeToFile:[self filename] atomically:YES];
}
- (BOOL) isNotFirstTime {
return [[NSFileManager defaultManager] fileExistsAtPath:[self filename]];
}
You could store in your property store a boolean value saying whether it's the first time or not, then check that on application start.