Get data from a PList into UITableView? - objective-c

I want to maintain a list of records, for each one I maintain the same type of data. I want to use this data in 2 different places:
UITableView that takes from each record the "Name" value
UIViewController that takes all the data to use in different fields.
I assume I should be using a plist to store the data; I also assume that the object that should be receiving the data for the UITableView is NSArray so I can use the cellForRowAtIndexPath method to create the table automatically.
So i created the plist "PLForArr.plist":
It seems that i can only get a NSDictionary when calling the plist using
NSString *path = [[NSBundle mainBundle] pathForResource:#"PLForArr" ofType:#"plist"];
NSArray * myArr = [[NSArray alloc] initWithContentsOfFile:path]; //doesn't work...
NSDictionary * myDict = [[NSDictionary alloc] initWithContentsOfFile:path]; //does work, but who to I make a NSArray out of it / or get the data from here to the UITableView?
I understand that i don't understand something basic here... Can someone take me step by step on:
How to keep the data - should I use plist or something else? Should I have a main record of type array (as I did on the example plist here) or can I just keep it as these Dictionaries without the unnecessary MyArr that I used considering the UITableView end target?
How to call the data - Is there a way to get it into a NSArray or must it get into a NSDictionary?
How to call it into the the UITableView - Can I fill in the lines using a NSDictionary?

Storing the data is an Array or a Dictionary is up to you. But if you want to make changes to it over time you can't store it in the main bundle.
Your pList file is a dictionary that contains an array. See code example below.
You will have to store the dictionary in an array for the data source for your table. See code example below.
Assuming that your UITableView's data source is called tableArray. You can use tableArray to fill in the information in the table and your view. Oh yeah,
NSString *path = [[NSBundle mainBundle] pathForResource:#"PLForArr" ofType:#"plist"];
NSDictionary *myDict = [[NSDictionary alloc] initWithContentsOfFile:path];
NSArray *myArray = [myDict objectForKey:#"MyArray"];
self.tableArray = [myArray copy];
[myArray release];
[myDict release];
This goes in tableView: cellForRowAtIndexPath:
cell.text = [[tableArray objectAtIndex:row]objectForKey:#"Obj Name"];

Storing your data either in a dictionary, or in an array is up to you. Depending on the kind of data you have, you will consider storing unordered collection of objects (dictionary), accessing the entries with keys; or rather in ordered collection (array), using numeric indexes.
It's fine to get arrays from property list files, but the root (top level) object is a dictionary (in the screenshot, "MyArr" isn't the top-level object, it is the key for accessing your array in the top-level dictionary). To get your array from it, simply alloc/init the plist dictionary the way you did, and access the array entry using its key ([myDict objectForKey:#"MyArr"]). Otherwise make sure you set the root object of the property list to be an array, and retry NSArray's initWithContentsOfFile:
The real question seems to be How can I fill the cells with my data ? The table views ask its delegate and dataSource about how many sections, rows in a section, to display. Based on these numbers, it will ask the dataSource for cells. Once again depending on the storage type you've chosen, you will implements these methods a little bit differently, but the concepts remain.
You will probably want to read documentation about :
Property List
Table views

Related

Passing a simple NSMutableArray change original

I am having hard time with a simple array that i want to pass .
I have a class with some NSMutableArray that i pass to another class(the array is global from singleton)
[mgzm saveWithArray:[Globals sharedGlobals].allImages];
To this function :
-(void)saveWithArray:(NSMutableArray*)currentArray
{
dataToSave=[[NSMutableArray alloc] init]; //local
dataToSave=[currentArray mutableCopy]; //copy
Than i saw that is is changing the original array which i don't want .
So i did this :
dataToSave=[[NSMutableArray alloc] initWithArray:currentArray copyItems:YES];
Which result in a situation that i can't change the dataToSave array get get a crash when trying to.(it needs to be changed).
Than i did this :
for(NSMutableDictionary *dic in currentArray)
[dataToSave addObject:dic];
Which again if i change dataToSave it change also the original array (?! )
Is there a way in this language to COPY array without changing the original one ????
When you make a copy of a mutable array, changing the array copy does not change the original array, not the objects inside the array. Here is what happens when you call [currentArray mutableCopy]:
The two arrays are pointing to the same objects. If you remove an object from the copy, the original array would still have it. However, if you modify the object itself (say, change the name of A to X) the change will reflect on the object in the original array, because it is the same object.
Here is what you want to happen:
Now the two arrays are completely independent of each other. This is the effect that you achieve when you call
dataToSave=[[NSMutableArray alloc] initWithArray:currentArray copyItems:YES];
However, there is a catch: in order for this to work, the objects inside the array must conform to NSCopying protocol, otherwise the code is going to crash.
To fix this, make sure that the objects inside NSMutableArray implement NSCopying. Here is an answer that explains how it is done.
The problem is although you are creating a new array the NSDictionary objects within the new array are still the same ones. So you need to make copies of the NSDictionary objects, you we're close but you need to do something like this...
-(void)saveWithArray:(NSMutableArray*)currentArray
{
dataToSave=[[NSMutableArray alloc] init]; //local
for(NSMutableDictionary *dic in currentArray)
[dataToSave addObject:[NSDictionary dictionaryWithDictionary:dic];
}

Why NSMutableArray doesn't get sorted?

NSFileManager *fileManager= [[NSFileManager alloc]init];
NSDirectoryEnumerator *myEnumerator= [fileManager enumeratorAtPath:[[theFolder URLByDeletingLastPathComponent]path]];
int f,size=0;
NSMutableArray *dirList=[[NSMutableArray alloc]init];
NSString *fpath;
while (fpath=[myEnumerator nextObject])
{
[dirList addObject:fpath];
}
[dirList sortedArrayUsingSelector:#selector(localizedStandardCompare:)];
dirList contains filenames like "name_0012345.tif". Despite the sort the array doesn't contain the file in the order i would see in the finder sorting by name.
-sortedArrayUsingSelector: is actually an NSArray method that returns a new sorted array (which you promptly ignore). You mean to use -sortUsingSelector:, which is an NSMutableArray method that rearranges the existing array itself.
It's pretty common in Cocoa to have one method for returning a modified immutable version of an object, and another method for modifying the mutable object itself (-stringByAppendingString: and -appendString:, for example).
From NSArray Class Reference
sortedArrayUsingSelector:
Returns an array that lists the receiving array’s elements in ascending order, as determined by the comparison method specified by a given selector.

Updating values on NSArray

I'v run into such problem. I need to update the values in my NSArray. And don't know a way to do it. Here's my array
NSArray *arrayWithInfo = [[NSArray alloc] initWithObjects:AMLocalizedString(#"Status", nil),AMLocalizedString(#"Call", nil),AMLocalizedString(#"Location", nil),AMLocalizedString(#"Control", nil),AMLocalizedString(#"Sim", nil),AMLocalizedString(#"Object", nil),AMLocalizedString(#"Info", nil),nil];
self.dataArray = arrayWithInfo;
[arrayWithInfo release];
To be more specific I have tableview initialized with this array. There is a possibility for user to use different localized strings, so I have to update it. By using [tableview reloadData]; i'v got the table to update, but the values in NSArray stay the same as they were initialized in first place.
So how to make array look up at the strings once again and get their new values?
Use NSMutableArray instead of NSArray
NSMutableArray (and all other classes with Mutable in the name) can be modified.
You should be using an NSMutableArray. Doing so will allow you to change its values after instantiation.
Your array doesn't need to be mutable here as the array seems to be all or nothing. You dont mention the requirement to delete some objects and not others. NSMutableArray isn't needed. You want to write a lazy loading getter method for the array which reinstantiates it if the array doesnt exist.
-(NSArray *)dataArray{
if (_dataArray){
return _dataArray;
}
_dataArray = NSArray *arrayWithInfo = [[NSArray alloc] initWithObjects:AMLocalizedString(#"Status", nil),AMLocalizedString(#"Call", nil),AMLocalizedString(#"Location", nil),AMLocalizedString(#"Control", nil),AMLocalizedString(#"Sim", nil),AMLocalizedString(#"Object", nil),AMLocalizedString(#"Info", nil),nil];
return _dataArray;
}
Then when you want to reload the tableView
self.dataArray = nil;
[tableView reloadData];
this destroys the old array, forcing it to be remade but with the new localisation.
EDIT:
The issue is the array isn't storing the statement AMLocalizedString(#"Status", nil) its storing the result of that statement, which is the localised string itself. There is no way to make the array re-evaluate that statement without either re-creating the whole array again or using an NSMutableArray and changing all the objects. The lazy loading getter method is more in the objective-c style.
You need to use the NSMutableArray. The NSArray is immutable.

Objective-C strange plist to dictionary behavior

i have a really simple plist which is loaded into a NSDictionary. However, when i try to access a specific value, no data is available.
This is how my plist is structured:
edit* xml got messed up. You can take a look at it at pastebin:
http://pastebin.com/C419ZVeJ
Here i load the plist:
NSString *bundlePath = [[NSBundle mainBundle] bundlePath];
NSString *finalPath = [bundlePath stringByAppendingPathComponent:#"Test.plist"];
NSDictionary *metaPlistData = [NSDictionary dictionaryWithContentsOfFile:finalPath];
NSDictionary *meta = [metaPlistData valueForKey:#"meta"];
NSDictionary *assets = [meta valueForKey:#"sd"];
(i have removed the line where i access the key=1 entry)
When i inspect the "meta" dictionary and the "assets" dictionary in gdb, "meta" contains the required entries. However, assets always is nil. I am really lost here.. any ideas why? I load data from plists using this approach at other locations in my code and it has never been a problem.
On first blush, it looks like you're off by one layer in the hierarchy. "sd" isn't a key in the "meta" dictionary, it's a key in the "1" dictionary. Try this:
NSDictionary *meta = [metaPlistData objectForKey:#"meta"];
NSDictionary *one = [meta objectForKey:#"1"];
NSDictionary *assets = [one objectForKey:#"sd"];
Note too that you should be using -objectForKey: for dictionary access (-valueForKey: will probably work in this context but it belongs to the key-value coding mechanism which is something a little different.)
(Also, not sure if this is just a paste issue, but your plist looks incomplete.)

Bidirectional Map in Cocoa

Cocoa provides NSDictionary, which essentially is an associative array.
Is there a nice way to get bidirectional associativity? i.e. one way would have been if NSDictionary had a keyForObject: method which mirrored the behavior of objectForKey:.
I don't really care if NSDictionary is not the way to get this. I know NSDictionary does provide keysOfEntriesPassingTest: but it returns an NSSet * which doesn't look very clean for the kind of thing I want to have.
Here is an example:
fieldMap = [[NSDictionary alloc] initWithObjectsAndKeys:
#"c0",#"synckey",
#"c1",#"wi_id",
#"c2",#"wi_parent_id",
#"c3",#"wi_type",
#"c4",#"wi_rh_task",
#"c5",#"decision_key",
#"c6",#"wi_stat",
#"c7",#"wi_prio",
#"c8",#"wi_cd",
#"c9",#"wi_ct",
#"cb",#"wi_dh_stat",
#"cc",#"wi_user",
nil];
I want to translate between c0 and synckey and back, and ditto for the other fields.
The closest thing for what you're after is, I believe allKeysForObject:. This returns an NSArray containing the keys corresponding to all occurrences of a given object in the dictionary. Obviously if the object is in the dictionary only once, the NSArray will contain only one object.
So with your example:
fieldMap = [[NSDictionary alloc] initWithObjectsAndKeys:
#"c0",#"synckey",
#"c1",#"wi_id",
#"c2",#"wi_parent_id",
#"c3",#"wi_type",
#"c4",#"wi_rh_task",
#"c5",#"decision_key",
#"c6",#"wi_stat",
#"c7",#"wi_prio",
#"c8",#"wi_cd",
#"c9",#"wi_ct",
#"cb",#"wi_dh_stat",
#"cc",#"wi_user",
nil];
This additional code would return an array containing 1 string object evaluating to #"c7":
NSArray *keyArray = [fieldMap allKeysForObject:#"wi_prio"];
[Aside: Note that this would only work here because of how the compiler works; it takes all occurences of #"wi_prio" and makes them the same object. If instead you had perhaps loaded the dictionary from disk etc, this approach will not work for NSStrings. Instead you should probably use allKeys and then iterate through them, comparing with [mystring isEqualToString:anotherString].]
The CHDataStructures framework has CHBidirectionalDictionary.