UITableview and datasource as NSMutableArray - iphone

I'm new to iOS dev and I followed a tutorial that was a simple UITableview and a detail view.
This sets up my Array:
- (void)viewDidLoad
{
[self setupArray];
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)setupArray
{
states = [[NSMutableDictionary alloc]init];
[states setObject:#"Thing 1" forKey:#"Subject 1"];
[states setObject:#"Thing 2" forKey:#"Subject 2"];
[states setObject:#"Thing 3" forKey:#"Subject 3"];
[states setObject:#"Thing 4" forKey:#"Subject 4"];
datasource = [states allKeys];
}
I have working cells and detail views. How do I add more objects to my keys? Is that possible? I need each subject [key] to have many attributes (i.e. a thing, a person, a place, a color)...
Can you break this down to the most simple terms for me? Thanks!

I'm not sure if I understand your question, but each key can have only one object associated with it. In your case, you're using an NSString object. If you replaced the NSString with some object that you create, say AnObjectWithAThingAndAPersonAndAPlace, you could have multiple attributes associated with each key.
I think I understand what you want now. What you want is not an object with arrays associated to it, but an array of objects. You can do it with NSDictionary objects.
- (void)setupArray
{
NSMutableArray *objectArray = [[NSMutableArray alloc] init];
NSMutableDictionary *object1 = [[NSMutableDictionary alloc] init];
[object1 setObject:#"Apple" forKey:#"thing"];
[object1 setObject:#"Alex" forKey:#"person"];
[object1 setObject:#"Alabama" forKey:#"place"];
[object1 setObject:#"Azure" forKey:#"color"];
[objectArray addObject:object1];
NSMutableDictionary *object2 = [[NSMutableDictionary alloc] init];
[object2 setObject:#"Banana" forKey:#"thing"];
[object2 setObject:#"Bill" forKey:#"person"];
[object2 setObject:#"Boston" forKey:#"place"];
[object2 setObject:#"Blue" forKey:#"color"];
[objectArray addObject:object2];
datasource = [NSArray arrayWithArray:objectArray];
}
Then in your UITableViewDataSource method
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSInteger row = [indexPath row];
NSDictionary *object = [datasouce objectAtIndex:row];
...
}
and you can retrieve all the strings for that object.
If I were to do something like this, I would probably create a plist file containing the array. Then your setupArray method could look like this:
- (void)setupArray
{
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"YourFileName" ofType:#"plist"];
NSDictionary *plistData = [NSDictionary dictionaryWithContentsOfFile:filePath];
datasource = (NSArray*)[plistData objectForKey:#"ObjectsForTableView"];
}
I though I would add a few more comments...In case it isn't obvious, the objects you add to your dictionary don't have to be NSStrings, they can be any object, such as an NSNumber, which may be useful for you in the case of your baseball players. Also, you may wish to create a custom player object instead of using an NSDictionary. And you may want to have something like a Core Data database where the players are stored and retrieved (instead of hard coding them or getting them from a plist file). I hope my answer can get you started on the right path though.

RIght now your datasource object is an NSArray. You need to make it an NSMutableArray. Declare it as an NSMutableArray in your header file and then you can do this:
datasource = [[states allKeys] mutableCopy];
[datasource addObject:whatever];
But, it sounds like the structure you are actually looking for is an NSMutableArray of NSDictionary objects. Like this:
NSDictionary *item = [NSDictionary dictionaryWithObjectsAndKeys:#"object1", #"key1", #"object2", #"key2", nil];
[datasource addObject:item]
;

There are numerous ways (better than your given example) to do this. But I'll follow your example.
You are assigning an NSString Object to your keys.
What you can do is create a class Thing that contains all your attributes. and assign an instance of that class to your keys. ie.
[states setObject:myThingObject forKey:#"Subject 4"];
Then pass the myThingObject to your Detail View.
UPDATE:
Thing class contains the following properties:
- person
- place
- color
- thingName
So,
[states setObject:#"Thing 1" forKey:#"Subject 1"];
becomes
[states setObject:firstObject forKey:#"Subject 1"];
[states setObject:secondObject forKey:#"Subject 2"];
Note firstObject & secondObject are instances of your Thing class
To read more about classes in Objective-c visit:
https://developer.apple.com/library/ios/#referencelibrary/GettingStarted/Learning_Objective-C_A_Primer/_index.html

Related

incompatible pointer types assigning to nsarray from nsdictionary

I'm new to iPhone development and have had great success with with answers from here so I am hoping to receive help directly. I am reading data into a tableview from a plist. The application works fine but I get 2 warnings when I compile. I know why I get the errors but I have been unsuccessful with resolving the issues. Although this app works I really would like to resolve the warnings efficiently. When I tried changing the NSDictionary to NSArray the warning goes away but the table is no longer populated.
Any help would be greatly appreciated.
Staff and Data are defined as NSArray in the Delegate .h file. The warnings show in the delegate .m file below.
My Delegate has the following:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
// Add the tab bar controller's current view as a subview of the window
NSString *Path = [[NSBundle mainBundle] bundlePath];
NSString *DataPath = [Path stringByAppendingPathComponent:#"Data.plist"];
NSString *SPath = [[NSBundle mainBundle] bundlePath];
NSString *StaffPath = [SPath stringByAppendingPathComponent:#"Staff.plist"];
NSDictionary *tempDict = [[NSDictionary alloc] initWithContentsOfFile:DataPath];
**self.data = tempDict;**
[tempDict release];
NSDictionary *staffDict = [[NSDictionary alloc]initWithContentsOfFile:StaffPath];
**self.staff = staffDict;**
[staffDict release];
In my staff ViewController I have the following:
if(CurrentLevel == 0) {
//Initialize our table data source
NSArray *staffDict = [[NSArray alloc] init];
self.tableDataSource = staffDict;
[staffDict release];
Midwest_DigestiveAppDelegate *AppDelegate = (Midwest_DigestiveAppDelegate *)[[UIApplication sharedApplication] delegate];
self.tableDataSource = [AppDelegate.staff valueForKey:#"Rows"];
}
else
self.navigationItem.title = CurrentTitle;
An NSArray holds a one dimensional list of items where an NSDictionary maps keys to values.
Array:
[a, b, c]
Dictionary:
{#"a" = #"first item", #"b" = #"second item"}
Could you declare data as NSDictionary *data; and populate it as data = [[NSDictionary alloc] initWithContentsOfFile:DataPath];
You then access values in the dictionary with [data valueForKey:#"key"]
Everything in your code suggests that the staff and data properties are NSDictionary instances. You initialize them to dictionary objects and you reference them as dictionary objects. Why then are you declaring them as NSArray objects?
You should change how they are declared so they are NSDictionary in your header file rather than NSArray. That seems to me the most logical way to remove your warnings.
This should still work assuming the contents of your "staff" NSDictionary has a key named "Rows" whose value is an NSArray. The code you have to initialize self.tableDataSource with an empty NSArray seems redundant, as you immediately overwrite the value with the
self.tableDataSource = [AppDelegate.staff valueForKey:#"Rows"];
line in your code

help on sorting NSMutableDictionary strings that are URL images

i want to populate the tablecell with title and imageurl from xml list.
i manage to store the title (NSMutableDictonary *sections )and imageURL (NSMutableDictonary *sectionsImg) into 2 NSMutableDictionary respectively.
/*******This is in viewDidLoad***/
Directory *allDirectory = [appDelegate.directories objectAtIndex:0];
for (allDirectory in appDelegate.directories)
{
NSDictionary *dica = [NSDictionary dictionaryWithObject:allDirectory.dirTitle forKey:#"dirTitle"];
NSDictionary *dico = [NSDictionary dictionaryWithObject:allDirectory.imageURL forKey:#"imageURL"];
[dirName addObject:dica];
[dirImage addObject:dico];
//NSLog(#"dic of items : %#",dirImage);
}
for (allDirectory in appDelegate.directories)
{
//retrieve the first letter from every directory title (dirTitle)
NSString * c = [allDirectory.dirTitle substringToIndex:3];
NSString * m = allDirectory.imageURL;
found = NO;
find = NO;
for (NSString *str in [self.sections allKeys])
{
if ([str isEqualToString:c])
{
found = YES;
}
}
for (NSString *stra in [self.sectionsImg allKeys])
{
if([stra isEqualToString:m])
{
find = YES;
}
}
if (!found)
{
[self.sections setValue:[[NSMutableArray alloc]init] forKey:c ];
[self.sectionsImg setValue:[[NSMutableArray alloc]init] forKey:m];
}
if (!find)
{
[self.sectionsImg setValue:[[NSMutableArray alloc]init] forKey:m];
}
}
for (NSDictionary *directory in dirName)
{
[[self.sections objectForKey:[[directory objectForKey:#"dirTitle"] substringToIndex:3]] addObject:directory];
//NSLog(#"hehehe have : %#",sections);
}
for (NSDictionary *directoryImg in dirImage)
{
//[[self.sectionsImg objectForKey:[[directoryImg objectForKey:#"imageURL"] substringFromIndex:0]] addObject:directoryImg];
[[self.sectionsImg objectForKey:[directoryImg objectForKey:#"imageURL"]] addObject:directoryImg];
//NSLog(#"HOHOHO have : %#",sectionsImg);
}
And on cellForRowAtIndexPath i declare a dictionary
NSDictionary *dictionary = [[self.sections valueForKey:[[[self.sections allKeys] sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)] objectAtIndex:indexPath.section]] objectAtIndex:indexPath.row];
cell.textLabel.text = [dictionary objectForKey:#"dirTitle"];
but when i tried to declare a dictionary for imageURL
NSDictionary *dictionaryImg = [[self.sectionsImg valueForKey:[[[self.sectionsImg allKeys] sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)] objectAtIndex:indexPath.section]] objectAtIndex:indexPath.row];
it gives me a error :
Terminating app due to uncaught exception 'NSRangeException', reason: '* -[NSMutableArray objectAtIndex:]: index 1 beyond bounds [0 .. 0]'
any idea why? the logic is supposed to be the same where xml title and url can be retrieve and be displayed. Title is retrievable but imageURL is not. Help is deeply appreciated !
You are trying to sort an array... except for the fact your array isn't an array, but a NSDictionary.
Your code isn't the best at the moment. Your getting the idea of Dictionaries wrong and may be confusing them with arrays, so my best guess is your quite new to programming into objective-c.
You have two lists of things, if I'm not mistaken. The first list is the list of names, and the second list is an image corresponding with that name.
Below I'm going to do two things:
Firstly, I'm giving you two ways on how to fix your problem. It has a sample code included and gives you a small explanation with it. The possibility exist you don't understand parts of what I describe. In that case, you should;
Check out the link I described below the two solutions. It has a tutorial which makes you understand everything about arrays, dictionaries, tables and, as a bonus, XML-parsing.
So, in my opinion, you can do two things:
The first one is using an array of NSDictionaries. You'd be using a code which looks like:
NSMutableDictionary *itemOne = [[NSMutableDictionary alloc] init];
NSMutableDictionary *itemTwo = [[NSMutableDictionary alloc] init];
NSMutableArray *listOfAll = [[NSmutableArray alloc] init];
NSString *itemOneName = [[NSString alloc] initWithFormat:#"This is picture 1"];
NSString *itemTwoName = [[NSString alloc] initWithFormat:#"This is picture 2"];
NSData *imageOneData = [[NSData alloc] initWithContentsOfURL: [NSURL URLWithString: #"http://myurl/mypic1.jpg"]];
NSData *imageTwoData = [[NSData alloc] initWithContentsOfURL: [NSURL URLWithString: #"http://myurl/mypic2.jpg"]];
UIImage *itemOneImage = [UIImage imageWithData: imageOneData];
UIImage *itemTwoImage = [UIImage imageWithData: imageTwoData];
[itemOne setObject:itemOneNameString forKey:#"Name"];
[itemOne setObject:itemOneImage forKey:#"Image"];
[itemTwo setObject:itemTwoNameString forKey:#"Name"];
[itemTwo setObject:itemTwoImage forKey:#"Image"];
[listOfAll addObject:itemOne];
[listOfAll addObject:itemTwo];
Anything can be filled using that array. Just use something with a for-loop to iterate through your array.
for (int i = 0; i < [listOfAll count]; i++)
{
NSMutableDictionary *currentItem = [[NSMutableDictionary alloc] initWithDictionary:[listOfAll objectAtIndex:i]];
//Do something with that current item
}
You can also use that index in your tableView. In that case, you have to use your variable section instead of i to get your desired index.
The second one is using two arrays. Imagine you get an image named imageOne with the text imageName. Then you should use:
NSMutableArray *nameList = [[NSMutableArray alloc] init];
[nameList addObject: imageName];
NSMutableArray *imageList = [[NSMutableArray alloc] init];
[imageList addObject: imageOne];
If you want to use a certain item out of those lists, you just have to use the same indexnumber.
For example:
[theTitleLabel setText:[[NSString alloc] initWithFormat:#"%#", [nameList objectAtIndex:x]]];
[theImageView setImage:[imageList objectAtIndex:x]];
Make sure the x's are the same number.
I understand this is all a lot of information, especially if you're new to Objective - C. A tutorial exists which gives you a lot of information about how to use arrays, dictionaries and table views. As a bonus, you get to know a little about XML-parsing.
I suggest you walk through that tutorial and do everything and read everything it says. This should give you a nice start into the world of programming in iPhones.
Good luck!

How to dissect and reorganize info in an NSDictionary

So I have an array of NSDictionaries, each NSDictionary has a bunch of key/value pairs pertaining to aspects of a photo (from Flickr).
I'm making an app that has a UITableViewController whose cells should be each of the different categories of the photos. So in pseudocode, I'm trying to construct a new NSDictionary (with keys being categories of photos, values being the NSDictionaries of the photos that contains that key). I'm iterating through each NSDictionary in the initial array, getting the category tags, and saying, if my new NSDict doesn't contain this key, make a new key to an empty array. Then add the current NSDict to that array. I'm getting consistent errors, not sure why.
Here's the diluted code.
photoList = [FlickrFetcher photosWithTags:[NSArray arrayWithObjects: #"CS193p_SPoT", nil]];
NSLog(#"%#", photoList);
categories = [[NSDictionary alloc] init];
NSArray *temp = [[NSArray alloc] init];
for (id obj in photoList) {
temp = [[obj objectForKey:#"tags"] componentsSeparatedByString:#" "];
for (id string in temp) {
if (![categories objectForKey:string]) {
NSMutableArray *arr = [[NSMutableArray alloc] init];
[categories setObject:arr forKey:string];
//[arr release];
}
NSMutableArray *photos = [categories objectForKey:string];
[photos addObject:obj];
[categories setObject:photos forKey:string];
}
}
Thanks!
NSDictionary doesn't have a method setObject:forKey:. You need an NSMutableDictionary.
self.categories = [NSMutableDictionary dictionary];
Other than that, please do use Joost's excellent rewrite of your code.
SIGABRT, just so you know, most likely means that an assertion somewhere failed. In this case, it may be an assertion all the way down in CoreFoundation*; CF checks for mutability when you try to access a dictionary like that and causes an interrupt if the object isn't mutable.
*I have just learned about the CF source's availability recently and have been looking through it, so this may be just "new thing" bias and incorrect.
I don't notice any errors (syntax-errors, that is) in your code, however here is an updated piece of code which has been implemented a bit cleaner (and without memory leaks)
self.photoList = [FlickrFetcher photosWithTags:[NSArray arrayWithObjects: #"CS193p_SPoT", nil]];
NSLog(#"%#", photoList);
self.categories = [NSDictionary dictionary];
for (NSDictionary *obj in photoList) {
NSArray *temp = [[obj objectForKey:#"tags"] componentsSeparatedByString:#" "];
for (NSString *string in temp) {
NSMutableArray *photos = [categories objectForKey:string];
if (!photos) {
photos = [NSMutableArray array];
[categories setObject:photos forKey:string];
}
[photos addObject:obj];
}
}
If it's not working please tell us the exact warning, and were it is caused.

Updating NSMutableArray of dictionary objects

I have a NSMutableArray (i.e. "stories") of dictionary items.
I can read the dictionary items in the array just fine, e.g.
[[[stories objectAtIndex: b] objectForKey: #"title"]
But, now I am trying to update (i.e. replace) a couple of objects e.g. "title" & "matchingword", but I cannot find the right code. Any suggestions are much appreciated.
I tried this, but it seems to be adding entirely new objects to the array
NSMutableDictionary *itemAtIndex = [[NSMutableDictionary alloc]init];
[itemAtIndex setObject:[[placesArray objectAtIndex:a]objectAtIndex:0] forKey:#"reference"];
[stories replaceObjectAtIndex:x withObject:itemAtIndex]; // replace "reference" with user's unique key
[itemAtIndex release];
I also tried this (but didn't work either):
//NSMutableDictionary *itemAtIndex2 = [[NSMutableDictionary alloc]init];
//[itemAtIndex2 setObject:[separatePlaces objectAtIndex:x] forKey:#"matchingword"];
//[stories insertObject:itemAtIndex2 atIndex:x]; // add the unique matching word to story
//[itemAtIndex2 release];
Help appreciated. Thanks.
You need to grab the dictionary you want to mod.
NSMutableDictionary *temp = [stories objectAtIndex: b];
The change the value:
[temp setObject:#"new Info" forKey:#"title"];

Adding 2 keyvalues to list from JSON object

I want to append 2 key values from JSON object to my list in iPhone app. Below is my code for that,
SBJsonParser *jsonParser = [[[SBJsonParser alloc] init] autorelease];
NSString *jsonString=[[NSString alloc] initWithContentsOfURL:[NSURL URLWithString:#"http://test/json/json_data.php"]];
id response = [jsonParser objectWithString:jsonString error:NULL];
NSDictionary *feed = (NSDictionary *)response;
list = (NSArray *)[feed valueForKey:#"fname"];
the above code properly displays the value from fname but what do i do if i want to add lname to it. for eg, my object is
[{"fname":"Bill","lname":"Jones"},{"fname":"John","lname":"Jacobs"}]
i want to display names as Bill Jones, John Jacobs and so on in the list. Currently it only displays Bill, John..I tried doing something like #"fname"#lname but it wont work..Can anybody please help me..
An observation: the response from the JSON parser is not a dictionary, but an array given the string you pass in. Your code works because -valueForKey: is something an array will respond to. The array sends -valueforKey: to each element and builds an array out of the responses.
There are two ways you can do what you want (at least)
Iterate through the array explicitly
NSMutableArray* list = [[NSMutableArray alloc] init];
for (id anObject in response)
{
[list addObject: [NSString stringWithFormat: #"%# %#",
[anObject objectForKey: #"fName"],
[anObject objectForKey: #"lname"]]];
}
Add a category to NSDictionary
#interface NSDictionary(FullName)
-(NSString*) fullName;
#end
#implementation NSDictionary(FullName)
-(NSString*) fullName
{
return [NSString stringWithFormat: #"%# %#",
[self objectForKey: #"fName"],
[self objectForKey: #"lname"]];
}
#end
Then your existing code changes to
list = (NSArray *)[feed valueForKey:#"fullName"];