Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

trey5498

macrumors regular
Original poster
Jun 16, 2008
191
0
I have successfully populated a UIPickerView with the contents of a plist.

Here is the example of the list:
Code:
<dict>
	<key>States</key>
	<array>
		<dict>
			<key>StateName</key>
			<string>Alabama</string>
			<key>Est</key>
			<string>1819</string>
			<key>Cap</key>
			<string>Montgomery</string>
			<key>Pop</key>
			<string>4822023</string>
			<key>Large</key>
			<string>Birmingham</string>
			<key>Flag</key>
			<string>Alabama.jpg</string>
		</dict>

I have done this with:

Code:
NSString *StatePath = [[NSBundle mainBundle] pathForResource:@"States" ofType:@"plist"];
NSDictionary *StateDictionary = [[NSDictionary alloc] initWithContentsOfFile:StatePath];

_StatesNames = StateDictionary[@"States"];
_States = [_StatesNames valueForKeyPath:@"StateName"];

Now I am having issue in segueing this plist to to a Details ViewController. I am not sure how to capture each state in the above plist example for the selected state. Does anyone have an idea how to tackle that?
 
similar to what i did above:

Code:
NSString *StatePath = [[NSBundle mainBundle] pathForResource:@"States" ofType:@"plist"];
NSDictionary *StateDictionary = [[NSDictionary alloc] initWithContentsOfFile:StatePath];
 
similar to what i did above:

Code:
NSString *StatePath = [[NSBundle mainBundle] pathForResource:@"States" ofType:@"plist"];
NSDictionary *StateDictionary = [[NSDictionary alloc] initWithContentsOfFile:StatePath];

No, what I mean is something more like:
Code:
NSString *StatePath = [[NSBundle mainBundle] pathForResource:@"States" ofType:@"plist"];
NSDictionary *StatesDictionary = [[NSDictionary alloc] initWithContentsOfFile:StatePath];
NSArray *statesArray = [StatesDictionary objectForKey:@"States"];
NSDictionary *aStateDictionary = [statesArray objectAtIndex:0];
State *aState = [State stateFromDictionary:aStateDictionary];

where State is a custom-built class.

P.S. variable names such as StatePath and StateDictionary don't follow coding conventions, where variables start with lower-case. They look like class names, which normally start with upper-case. I'd suggest you name them statePath and stateDictionary instead.

Once you have the info in a State object, there are a number of ways to share that object with the Detail View Controller.
 
Plist information pulling?

I did not want to bump an old thread since it has been a bit since my last post.

I have successfully populated a UIPickerView with the contents of a plist and want to pull the information associated with it.

Here is the example of the list:

Code:
<dict>
	<key>States</key>
	<array>
		<dict>
			<key>StateName</key>
			<string>Alabama</string>
			<key>Est</key>
			<string>1819</string>
			<key>Cap</key>
			<string>Montgomery</string>
			<key>Pop</key>
			<string>4822023</string>
			<key>Large</key>
			<string>Birmingham</string>
			<key>Flag</key>
			<string>Alabama.jpg</string>
		</dict>
                 <dict>
			<key>StateName</key>
			<string>Alabama2</string>
			<key>Est</key>
			<string>1819</string>
			<key>Cap</key>
			<string>Montgomery</string>
			<key>Pop</key>
			<string>4822023</string>
			<key>Large</key>
			<string>Birmingham</string>
			<key>Flag</key>
			<string>Alabama.jpg</string>
		</dict>
..................

The <dict> inside the array shows up at item0, item 1 ...etc.... when I open the plist in xcode 5.

I can get the list of the state names and then I am able to use the picker to select a value of a single state and use it for other purposes.

What I am unable to do is to grab the rest of the keys. When I try:

Code:
NSString *StateDict = [NSString stringWithFormat:@"%@", [[NSUserDefaults standardUserDefaults] dictionaryForKey:SelectedState]];

I get null. I don't even get the item0, item1 and so forth. How would I grab the rest of the <dict> and then use it as an array. I tried looking up custom classes, however, I only come up with custom classes for TableViews, PickerViews, Labels, etc.... Ideally this would be perfect use of an array or a collection.

Can only one get me stated?
 
I did not want to bump an old thread since it has been a bit since my last post.

Mod voice: It wouldn't be considered a bump since you are adding new information. As such, I've merged it with the original thread.

I tried looking up custom classes, however, I only come up with custom classes for TableViews, PickerViews, Labels, etc....

What I meant by "custom class" is that you should create your own class to hold the state information. Since you didn't understand what I meant, can I ask what your comfort-level is with Objective-C / OOP programming? What resources have you used to learn the fundamentals? Please be as specific as possible.
 
My comfort level is this. I am a very confident coder in python, powershell and perl. I have also had some real world experience in C++ and Objective-C (although, nothing with Plists and mostly simple). I am currently taking a Mobile Programming class as I want to expand that knowledge and as I am a programmer by trade.... I naturally try to push the envelope to learn.

I did use collections before, which can be a type of class, but have never used them or created them in objective-c.

Thanks for the migration. I did not know if that would be considered a bump.. I have used another forum that did... but then again that was a long while ago.
 
I appreciate your patience with me, as I am not asking for you to do the work for me. I only ask for confirmation (or a slap in the back of the head with a point in the right direction). The only thing I can think that will work maybe to create an NSObject and then call it. I do believe that is what you meant.

Code:
stateDataClass.h

@interface stateDataClass : NSObject {}
	@property (nonatomic, copy) NSString *stateName;
	@property (nonatomic, copy) NSString *estDate;
	@property (nonatomic, copy) NSString *capital;
	@property (nonatomic, copy) NSString *largeCity;
	@property (nonatomic, copy) int *population;
	@property (nonatomic, copy) NSString *flagImage;
@end

And maybe create the rest of the searchable array this way?

Code:
NSString *StatePath = [[NSBundle mainBundle] pathForResource:@"States" ofType:@"plist"];
NSDictionary *StateDictionary = [[NSDictionary alloc] initWithContentsOfFile:StatePath];
NSMutableArray *stateDetails= [[NSDictionary alloc] init];

//Creates the list of States for the UIPickerView
_StatesNames = StateDictionary[@"States"];
_States = [_StatesNames valueForKeyPath:@"StateName"];

//Creates the array with the State details
for (NSDictionary *stateDict in _StatesNames) {
    stateDataClass *detail = [[stateDataClass alloc] init];
    detail.stateName = [stateDict objectForKey:@"StateName"];
    detail.estDate = [stateDict objectForKey:@"Est"];
    detail.capital = [stateDict objectForKey:@"Cap"];
    detail.largeCity = [stateDict objectForKey:@"Large"];
    detail.population = [stateDict objectForKey:@"Pop"];
    detail.flagImage = [stateDict objectForKey:@"Flag"];
    [stateDetails addObject:detail];
}

or do you happen to know a better way?
 
Last edited:
I appreciate your patience with me, as I am not asking for you to do the work for me. I only ask for confirmation (or a slap in the back of the head with a point in the right direction). The only thing I can think that will work maybe to create an NSObject and then call it. I do believe that is what you meant.

Code:
stateDataClass.h

@interface stateDataClass : NSObject {}
	@property (nonatomic, copy) NSString *stateName;
	@property (nonatomic, copy) NSString *estDate;
	@property (nonatomic, copy) NSString *capital;
	@property (nonatomic, copy) NSString *largeCity;
	@property (nonatomic, copy) int *population;
	@property (nonatomic, copy) NSString *flagImage;
@end

And maybe create the rest of the searchable array this way?

Code:
NSString *StatePath = [[NSBundle mainBundle] pathForResource:@"States" ofType:@"plist"];
NSDictionary *StateDictionary = [[NSDictionary alloc] initWithContentsOfFile:StatePath];
[B]NSMutableArray *stateDetails= [[NSDictionary alloc] init];[/B]

//Creates the list of States for the UIPickerView
_StatesNames = StateDictionary[@"States"];
_States = [_StatesNames valueForKeyPath:@"StateName"];

//Creates the array with the State details
for (NSDictionary *stateDict in _StatesNames) {
    stateDataClass *detail = [[stateDataClass alloc] init];
    detail.stateName = [stateDict objectForKey:@"StateName"];
    detail.estDate = [stateDict objectForKey:@"Est"];
    detail.capital = [stateDict objectForKey:@"Cap"];
    detail.largeCity = [stateDict objectForKey:@"Large"];
    detail.population = [stateDict objectForKey:@"Pop"];
    detail.flagImage = [stateDict objectForKey:@"Flag"];
    [stateDetails addObject:detail];
}

or do you happen to know a better way?

Looks pretty good to me. A couple of things: 1) you're not creating an NSObject; you're defining a new class that happens to be a subclass of NSObject, 2) see the line I highlighted; it's probably not what you want.
 
so after a long weekend of sitting at mechanic shops and finally car dealerships.. I have finally gotten back to this. Here is the update I got...

Code:
NSString *StatePath = [[NSBundle mainBundle] pathForResource:@"States" ofType:@"plist"];
    NSMutableDictionary *StateDictionary = [[NSMutableDictionary alloc] initWithContentsOfFile:StatePath];
    _stateDetails = [[NSMutableArray alloc] initWithCapacity:50];
    
    
    //Creates the list of States for the UIPickerView
    _StatesNames = StateDictionary[@"States"];
    _States = [_StatesNames valueForKeyPath:@"StateName"];
    
    //Creates the array with the State details
    for (NSDictionary *stateDict in _StatesNames) {
        stateDataClass *detail = [[stateDataClass alloc] init];
        detail.stateName = [stateDict objectForKey:@"StateName"];
        detail.estDate = [stateDict objectForKey:@"Est"];
        detail.capital = [stateDict objectForKey:@"Cap"];
        detail.largeCity = [stateDict objectForKey:@"Large"];
        detail.population = [stateDict objectForKey:@"Pop"];
        detail.flagImage = [stateDict objectForKey:@"Flag"];
        [_stateDetails addObject:detail];
    }

Now I am trying to pull that info out of the array by way of selecting the object through the pickerview with:

Code:
 NSString *SelectedState = [_States objectAtIndex:[_StatePickerView selectedRowInComponent:0]];

this returns the name of the state (IE: Alaska) and I can search the array created and find alaska, but I know there has to be a prefered method like

Code:
Example:
statPopLabel.text =  [_stateDetails valueForKey:SelectedState.population]

Am I close to the right track with this thought or where should I be leaning towards?
 
Code:
NSString *StatePath = [[NSBundle mainBundle] pathForResource:@"States" ofType:@"plist"];
    NSMutableDictionary *StateDictionary = [[NSMutableDictionary alloc] initWithContentsOfFile:StatePath];
    _stateDetails = [[NSMutableArray alloc] initWithCapacity:50];
    
    
    //Creates the list of States for the UIPickerView
    _StatesNames = StateDictionary[@"States"];
    _States = [_StatesNames valueForKeyPath:@"StateName"];
    
    //Creates the array with the State details
    for (NSDictionary *stateDict in _StatesNames) {
        [B][COLOR="Red"]stateDataClass *detail = [[stateDataClass alloc] init];[/COLOR][/B]
        detail.stateName = [stateDict objectForKey:@"StateName"];
        detail.estDate = [stateDict objectForKey:@"Est"];
        detail.capital = [stateDict objectForKey:@"Cap"];
        detail.largeCity = [stateDict objectForKey:@"Large"];
        detail.population = [stateDict objectForKey:@"Pop"];
        detail.flagImage = [stateDict objectForKey:@"Flag"];
        [_stateDetails addObject:detail];
    }

First, you're still not following common Objective-C coding conventions of class names starting with upper-case and variables starting with lower-case. For example, the above, highlighted line is confusing because, at first glance, stateDataClass and detail appear to be the same kind of thing. They're not. Your class name should be StateData. (No need for the "Class" suffix; avoid being self-referential). You should also go back and adjust StatePath, StateDictionary, _StatesNames and _States. I would also revisit your variable names since they can be confusing. For example, _States is an array that actually contains state names, but _StateNames is already taken.

Second, you now have a _stateDetails array containing stateDataClass StateData objects. You should access its members the same way you are accessing the members of your _States array. That is, why not use objectAtIndex:?
 
I know my naming convention isn't the best... In fact, to be honest in most of the code I have ever written (as in above mostly scripting) all is lowercase. I also tend to document inside the code whenever possible (usually after I know it works so I can go back and understand it when I look at it later down the road). I did not know the convention for Objective-C was what you said until this thread. The class I am taking isn't that clear on the subject. I will change it after I solve my issues.

You are also 100% right on the array names should be changed, but for the sake of this thread I kept it the same so others could easily follow.

With the _stateDetails holding 50 of these:
Code:
[0]	stateDataClass *
NSObject	
_stateName =	__NSCFString *	@"Alabama"
_estDate =	__NSCFString *	@"1819"
_capital =  	__NSCFString *	@"Montgomery"
_largeCity =	__NSCFString *	@"Birmingham"
_population =	__NSCFString *	@"4822023"
_flagImage =	__NSCFString *	@"Alabama.jpg"


and the code (example to get values):
Code:
NSString *stateTexas = @"Texas";
NSString  *objectOfTexas = [_stateDetails objectAtIndex:42];
NSUInteger indexOfTexas = [_stateDetails indexOfObject:stateTexas];
    
NSLog(@"Object at index 42 is: %@", objectOfTexas);  //returns the entire NSObject that holds all the information
NSLog(@"The Index of Texas is %tu", indexOfTexas);  //returns 2147483647

in theory an easy way to get index number of each state would be to (example):
Code:
//_stateNames is formerly _States from above and selectedState would be the value selected by the UIPickerView
NSUInteger indexOfState = [_stateNames indexOfObject:selectedState];  //returns 42

Then I could use that index along with the [_stateDetails objectAtIndex:indexOfState] to get the information, but how would I grab the details out of the array without creating a new one to populate the labels?

IE: Populate the stateCapitallbl.text

As you can see, I really am trying to challenge myself with things that will be relevant in real life and not what is in the class room just to get a passing grade. I want to learn it to be able to use it.
 
Code:
NSString *stateTexas = @"Texas";
NSString  *objectOfTexas = [_stateDetails objectAtIndex:42];
NSUInteger indexOfTexas = [_stateDetails indexOfObject:stateTexas];

_stateDetails in an array containing StateData objects, correct? Then objectAtIndex: isn't going to return an NSString object but a StateData object. Also, you are asking for the indexOfObject: for an object that doesn't exist in the array here. _stateDetails is an array of StateData objects, not NSString objects.

Anyways, if both your UIPickerView and _stateDetails are built using the same plist, than the index from the picker should provide the same state in the array, right?

...but how would I grab the details out of the array without creating a new one to populate the labels?

IE: Populate the stateCapitallbl.text

Have you studied how to access the properties of an object yet?
 
Since it has been about 10 years since the last time I ever needed to use a custom object class (or collections as they are called by other languages). I am a tad bit rusty at some areas.

Anyways, if both your UIPickerView and _stateDetails are built using the same plist, than the index from the picker should provide the same state in the array, right?

They are both built from the plist. I have created (before this thread) a simple go button that would show an alert with the state name (will replace it with a segue once I figure out the information grab) and use the following to obtain the name

Code:
//titleForRow function.  fills the UIPickerView and _stateNames is formally _States
return [_stateNames objectAtIndex:row];


//located in the button pressed function
NSString *selectedState = [_States objectAtIndex:[_statePickerView selectedRowInComponent:0]];



Have you studied how to access the properties of an object yet?

No and in this class, we will not be. Since personally I believe it is bad form to have information hard coded in arrays. I took it upon myself to learn plists.

Any function I need to look for to grab each information out of the NSObject?
 
Any function I need to look for to grab each information out of the NSObject?

Um, it's not an NSObject. It's a custom object, who class happens to be a subclass of NSObject. I think you're better off if you try to think of NSObject as little as possible.

Anyways, accessing the properties of an object is a basic concept that is covered pretty early on in most introductions to Objective-C. If your goal is to not "just to get a passing grade" but to "learn it to be able to use it", I would suggest at this point that you step away from the real coding and spend some time learning the fundamentals of Objective-C programming. This will help you in the long run because you'll want that foundation from which to build upon.
 
For future readers of this post:

Code:
    NSString *selectedState = [_stateNames objectAtIndex:[_statePickerView selectedRowInComponent:0]];
    NSUInteger indexOfState = [_stateNames indexOfObject:selectedState];
    NSString *objectOfState = [_stateDetails objectAtIndex:indexOfState];
    stateCapitallbl.text = [objectOfState valueForKey:@"_capital"];

That code will solve my issue. I am sure there is an easier way and I am looking into this subject more, since the class book just grazes the subject if at all.

All trial error and internet research. Thank you again for your patience.
 
Code:
    NSString *selectedState = [_stateNames objectAtIndex:[_statePickerView selectedRowInComponent:0]];
    NSUInteger indexOfState = [_stateNames indexOfObject:selectedState];
    [B][COLOR="Red"]NSString *objectOfState = [_stateDetails objectAtIndex:indexOfState];[/COLOR][/B]
    stateCapitallbl.text = [objectOfState valueForKey:@"_capital"];

I don't believe your [_stateDetails objectAtIndex:indexOfState] returns an NSString. And then calling valueForKey: on an NSString doesn't make sense.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.