views:

231

answers:

3

I'm trying to set up a UITableView which acts as a form. Each cell has within it a UILabel and a UITextField, so one cell is:

Name          <enter name>
^             ^
UILabel       UITextField

Now, I'm trying to populate the UILabel and the UITextField from a NSDictionary (from a plist), where it's organized like so:

Root            Type           Value
  Name          String
  Address       String
  City          String

  etc

But in order to get the right labels and textfields in the cellForRowAtIndexPath method, I would have to refer to the Dictionary numerically. Is there any way to do that?

+2  A: 

Strictly speaking, no.

What I tend to do is create a separate NSArray of keys, which are ordered so that things come out in the right sequence. eg:

NSDictionary *values = ...
NSArray *keys = ....

for (NSString *key in keys) {
    id value = [values objectForKey:key];
    ...
}

As others have pointed out, it is necessary to have an ordered list (ie, an NSArray) to match the way that a UITableView works. However, this doesn't have to be the data itself. When the data is a NSDictionary, or an NSManagedObject, there is a common idiom of editing that data in a form, which on iPhoneOS will almost always mean a UITableView.

In this case, you very probably won't want to show all the elements in the edited object, you will want to display titles for the fields, and will want to control the order of display. All of which means that this is a valid (not obfuscated) pattern for the situation.

In this particular case, the keys could be taken from the plist, but extracting them in a general way, although feasible by parsing the plist XML, is non-trivial.

Paul Lynch
Would this enumerate in the correct order?
cannyboy
It will enumerate in the order of the keys array. Which I hope is correct :-).
Paul Lynch
Just to be clear, I would have to set up my array to match the keys in the Dictionary so... keys = [NSArray arrayWithObjects:@"Name", @"Address", @"City", nil]; ... and then enumerate? So there is no way of getting the keys in the order that they are in the plist without naming and shaming them?
cannyboy
That's what you would need to do, yes. NSDictionary keys are unordered; you probably could parse the plist XML to extract the order, but it wouldn't be at all simple.
Paul Lynch
Plus, it would be a very obfuscated way of expressing what you want. You really should keep your row's data in an ordered data structure (read: an array)
Nikolai Ruhe
@cannyboy "So there is no way of getting the keys in the order that they are in the plist without naming and shaming them?" The problem is that there is no significance to the ordering of the keys in the plist. It's a representation of a dictionary. What nI would do is use allKeys to get the list of keys and then use a sort descriptor or other method to get them into a well defined order. You can back a table view with any collection object as long you can define a consistent ordering for it. It doesn't have to be an array.
JeremyP
I'm going to back my comments into the answer here, as this is a fairly significant discussion.
Paul Lynch
This discussion went nuts. Neither does it help to answer the original question ("Can I access dictionary elements by index?"), nor does it propose a sensible way of helping with the problem (ordered access to data for use in a table view). @Paul: Parsing the plist yourself? Come on, you don't really mean that?
Nikolai Ruhe
This answer does answer indexing dictionaries (no, see first line), and how to order dictionaries for use in forms, which is a generally encountered problem. And the response about extracting info from the plist is accurate - ie, it's non-trivial.
Paul Lynch
+4  A: 

No, since elements in an NSDictionary are not ordered.

You should really use an array as the model behind a table view data source. You can easily build one for your case:

NSArray* myArray = [myDict allKeys];

Of course, for your case, a special order would be neccessary. So you would rather build your array using literals:

NSArray* myArray = [NSArray arrayWithObjects:@"Name", @"Address", @"City", nil];
Nikolai Ruhe
i also suggest to use an array and have each element contain your data for each row
Tomen
It is a very common case to have a pseudo-dictionary (ie an NSManagedObject) that you want to present in a form for editing. Forcing the data into an array isn't a suitable approach for this case.
Paul Lynch
An array matches the requirements for a table view data source's model (providing indexed access to an ordered collection of objects). You can use it's elements to access other parts of your model but the way to access the data leads through objects in the array.
Nikolai Ruhe
+2  A: 

You should restructure your plist to be an array of dictionaries. The dictionary keys are the element that you need to populate and the values the text. Add each dictionary to the plist array in the order that you want it to appear on screen. This is effectively how the Settings application works. Saving the text entered by the user is very straightforward.

Your plist will look something like:

<root>
    <array>
        <dict>
            <key>labelText</key>
            <value>Name</value>
            <key>textFieldPromptText</key>
            <value>enter name</value>
            <key>textFieldText</key>
            <value>name entered by user</value>
        </dict> 
        <dict>
            <key>labelText</key>
            <value>Address</value>
            <key>textFieldPromptText</key>
            <value>enter street address</value>
            <key>textFieldText</key>
            <value>address entered by user</value>
        </dict>
        <dict>
            <key>labelText</key>
            <value>City</value>
            <key>textFieldPromptText</key>
            <value>enter city</value>
            <key>textFieldText</key>
            <value>city entered by user</value>
        </dict> 
        .
        .
        .
    </array>
</root>
falconcreek
Thanks for stating clearly what @cannyboy needs.
Nikolai Ruhe