How to set numberOfSectionsInTableView, titleForHeaderInSection using dynamic json data - iphone

I have some json data given below and I want to display it in a table where the section headers are Departments, Designing, Employees, Developing, Employees.
I used static data in numberOfSectionsInTableView, titleForHeaderInSection but I want to do it using dynamic data.
How can I do this?
{
"Departments":[
{
"name":"Designing",
"id":"1.1",
"Employees":[
{
"name":"Ramesh",
"id":"1.1.1",
"salary":"4lakhs"
},
{
"name":"Suresh",
"id":"1.1.2",
"salary":"4lakhs"
}
]
},
{
"name":"Developing",
"id":"1.2",
"Employees":[
{
"name":"Ram",
"id":"1.2.1",
"salary":"4lakhs"
},
{
"name":"Sam",
"id":"1.2.2",
"salary":"4lakhs"
}
]
}
}

first you have to create a NSDictionary from the string JSON object you specified.
NSDictionary *myDict = [NSJSONSerialization JSONObjectWithData:webData options:nil error:NULL];
Then in the table view delegate methods:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [[myDict valueForKey:#"Departments"] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [[[[myDict valueForKey:#"Departments"] objectAtIndex:section] valueForKey:#"Employees"] count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
return [[[myDict valueForKey:#"Departments"] objectAtIndex:section] valueForKey:#"name"];
}
Or at least I would try to set them like this.

Related

iPhone SDK: How To Create Sections On UITableView

I have this data that I need to put inside my UITableView but I'm getting confused on how to properly implement it. I cannot properly separate the values to segregate the Boys data to the Girls data.
{
"QUERY": {
"COLUMNS": [
"NAME",
"GENDER"
],
"DATA": [
[
"Anne",
"Girl"
],
[
"Alex",
"Boy"
],
[
"Vince",
"Boy"
],
[
"Jack",
"Boy"
],
[
"Shiela",
"Girl"
],
[
"Stacy",
"Girl"
]
]
},
"TOTALROWCOUNT": 6
}
I have this codes:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [genderArray count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
return [genderArray objectAtIndex:section];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [namesArray count];
}
namesArray has all the values returned by NAME while genderArray has all the values of GENDER. I'm getting confused.
AS you are getting confused, break your data into pieces. You want two arrays, one per section. So you want one array of boys names, and another array of girls names.
You can obtain this by iterating through the embedded DATA array.
Turn your data into an NSDictionary object. Your data looks like JSON so...
NSDictionary* myDict = [NSJSONSerialization JSONObjectWithData:myJsonData
options:0 error:&error];
Extract the data...
NSArray* dataArray = [myDict objectForKey:#"DATA"];
Iterate...
NSMutableArray* boys = [[NSMutableArray alloc] init];
NSMutableArray* girls = [[NSMutableArray alloc] init];
for (id person in dataArray) {
if ([[person objectAtIndex:1] isEqualToString:#"Girl"])
[girls addObject:[person objectAtIndex:0]];
else [boys addObject:[person objectAtIndex:0]];
}
Now you have two arrays, one for each of your table sections. Make an array of sections, and put these arrays into it:
NSArray* sections = [NSArray arrayWithObjects:boys,girls,nil];
Make a separate array for your section headers:
NSArray* headers = [NSArray arrayWithObjects:#"Boys",#"Girls",nil];
Now your data source methods look like this:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [sections count];
}
- (NSString *)tableView:(UITableView *)tableView
titleForHeaderInSection:(NSInteger)section
{
return [headers objectAtIndex:section];
}
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section
{
return [[sections objectAtIndex:section] count];
}
and finally
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
...
cell.textLabel.text = (NSString*)[[self.sections objectAtIndex:indexPath.section]
objectAtIndex:indexPath.row];

sorting the array in table view in iOS 6

Hello I have created one of my application in which i have implemented sorting functionality in a table view The sorting method is working fine on iOS 4 and 5 but when I try to test the application on iOS 6, it shows an error in the sorting method on iOS 6
Please help
Method :-
-(void)setupIndexData{
self.arrayOfCharacters =[[NSMutableArray alloc]init];
self.objectForCharacter=[[NSMutableDictionary alloc]init];
NSNumberFormatter *formatter =[[NSNumberFormatter alloc]init];
NSMutableArray *arrayOfNames =[[NSMutableArray alloc]init];
NSString *numbericSection = #"#";
NSString *firstLetter;
for (NSDictionary *item in self.mCompanyarray) {
firstLetter = [[[item valueForKey:#"Company"]description] substringToIndex:1];
// Check if it's NOT a number
if ([formatter numberFromString:firstLetter] == nil) {
/**
* If the letter doesn't exist in the dictionary go ahead and add it the
* dictionary.
*
* ::IMPORTANT::
* You HAVE to removeAllObjects from the arrayOfNames or you will have an N + 1
* problem. Let's say that start with the A's, well once you hit the
* B's then in your table you will the A's and B's for the B's section. Once
* you hit the C's you will all the A's, B's, and C's, etc.
*/
if (![objectForCharacter objectForKey:firstLetter]) {
[arrayOfNames removeAllObjects];
[arrayOfCharacters addObject:firstLetter];
}
[arrayOfNames addObject:item];
/**
* Need to autorelease the copy to preven potential leak. Even though the
* arrayOfNames is released below it still has a retain count of +1
*/
[objectForCharacter setObject:[[arrayOfNames copy] autorelease] forKey:firstLetter];
} else {
if (![objectForCharacter objectForKey:numbericSection]) {
[arrayOfNames removeAllObjects];
[arrayOfCharacters addObject:numbericSection];
}
[arrayOfNames addObject:item];
[objectForCharacter setObject:[[arrayOfNames copy] autorelease] forKey:numbericSection];
}
}
[formatter release];
[arrayOfNames release];
[self.mCompaniesTableView reloadData];
}
Thanks
I'd use UILocalizedIndexedCollation to sort and index your data. That way, your app can support multiple languages etc.
Note: I haven't tested the code below, but the theory is there.
First, create a #property to store the indexed data:
#property (nonatomic, strong) NSDictionary *indexedSections;
Set up your table like this:
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView
{
return [[UILocalizedIndexedCollation currentCollation] sectionIndexTitles];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
//we use sectionTitles and not sections
return [[[UILocalizedIndexedCollation currentCollation] sectionTitles] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [[self.indexedSections objectForKey:[NSNumber numberWithInteger:section]] count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
BOOL showSection = [[self.indexedSections objectForKey:[NSNumber numberWithInteger:section] count] != 0;
//only show the section title if there are rows in the section
return (showSection) ? [[[UILocalizedIndexedCollation currentCollation] sectionTitles] objectAtIndex:section] : nil;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
id object = [[self.indexedSections objectForKey:[NSNumber numberWithInteger:indexPath.section]] objectAtIndex:indexPath.row];
// configure the cell
}
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index
{
//sectionForSectionIndexTitleAtIndex: is a bit buggy, but is still useable
return [[UILocalizedIndexedCollation currentCollation] sectionForSectionIndexTitleAtIndex:index];
}
And finally index like this:
- (void) setupIndexData
{
// asynchronously sort
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
{
// create a dictionary to store an array of objects for each section
NSMutableDictionary *tempSections = [NSMutableDictionary dictionary];
// iterate through each dictionaey in the list, and put them into the correct section
for (NSDictionary *item in self.mCompanyarray)
{
// get the index of the section (Assuming the table index is showing A-#)
NSInteger indexName = [[UILocalizedIndexedCollation currentCollation] sectionForObject:[item valueForKey:#"Company"] collationStringSelector:#selector(description)];
NSNumber *keyName = [NSNumber numberWithInteger:indexName];
// if an array doesnt exist for the key, create one
NSMutableArray *arrayName = [tempSections objectForKey:keyName];
if (arrayName == nil)
{
arrayName = [NSMutableArray array];
}
// add the dictionary to the array (add the actual value as we need this object to sort the array later)
[arrayName addObject:[item valueForKey:#"Company"]];
// put the array with new object in, back into the dictionary for the correct key
[tempSections setObject:arrayName forKey:keyName];
}
/* now to do the sorting of each index */
NSMutableDictionary *sortedSections = [NSMutableDictionary dictionary];
// sort each index array (A..Z)
[tempSections enumerateKeysAndObjectsUsingBlock:^(id key, id array, BOOL *stop)
{
// sort the array - again, we need to tell it which selctor to sort each object by
NSArray *sortedArray = [[UILocalizedIndexedCollation currentCollation] sortedArrayFromArray:array collationStringSelector:#selector(description)];
[sortedSections setObject:[NSMutableArray arrayWithArray:sortedArray] forKey:key];
}];
// set the global sectioned dictionary
self.indexedSections = sortedSections;
dispatch_async(dispatch_get_main_queue() ,^{
// reload the table view (on the main thread)
[self.mCompaniesTableView reloadData];
});
});
}

Sorting NSDictionary from JSON for UITableView

I am having trouble trying to sort my NSDictionary in order, and it's been weeks and I still haven't figured it out, so I wish some one could give me a hand here...
NSDictionary data is from JSON and the data is already sorted from the server and it displayed in order in the JSON, but when it retrieved and converted to NSDicitionary, the order is all wrong...
This is the JSON
{
"categories":{
"unknownCategoryName":[
{ "key1":"value1",
"key2":"value2"
}],
"unknownCategoryName1":[
{ "key1":"value1",
"key2":"value2"
}],
"unknownCategoryName2":[
{ "key1":"value1",
"key2":"value2"
}],
"unknownCategoryName3":[
{ "key1":"value1",
"key2":"value2"
}]
}
}
The category quantity and its name will not be known until the JSON is received, so this is what I am using to get the count and setting up the tableView and the section
NSDictionary *results = [jsonString JSONValue];
NSDictionary *all = [results objectForKey:#"categories"];
self.datasource = all;
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [self.datasource count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
NSString *title = [[self.datasource allKeys] objectAtIndex:section];
return title;
}
And what I want to do is to list out the section and the section title as the order in the JSON.... rather than a chaos display like
unknownCategory3
unknownCategory1
unknownCategory
unknownCategory2
The "unknownCategory" is Product Category name, they are not in alphabet order, without any number, and they are already sorted and display in order in the JSON...
So, it would be great if you guys could help.
Thanks in advance...
Keys are not ordered in NSDictionaries, so you have to sort the keys first before using them in your table view. So, instead of using [self.datasource allKeys] to get your section titles, first create a sorted array: sorted = [[self.datasource allKeys] sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)], and then use that array to get the titles: title = [sorted objectAtIndex:section].
After Edit to answer a further question:
To use the sorted array to get the values you want into your table, I think this should be close. You would have to add a property, NSArray *sorted, to your .h file, and then this in your .m (this is assuming the structure of your json is as you posted above):
NSDictionary *results = [jsonString JSONValue];
NSDictionary *all = [results objectForKey:#"categories"];
self.datasource = all;
self.sorted = [[self.datasource allKeys] sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)];
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [self.sorted count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
NSString *title = [self.sorted objectAtIndex:section];
return title;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [[self.datasource valueForKey:[self.sorted objectAtIndex:section]]count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier];
}
NSArray *theArray = [self.datasource valueForKey:[self.sorted objectAtIndex:indexPath.section]];
NSString *text1 = [[theArray objectAtIndex:0] valueForKey:#"key1"];
NSString *text2 = [[theArray objectAtIndex:0] valueForKey:#"key2"];
cell.textLabel.text = [NSString stringWithFormat:#"%#\r%#",text1,text2];
cell.textLabel.numberOfLines=0; // this set the number of lines to unlimited
return cell;
}
You didn't say how you wanted to display two different values in one table row -- in this example, I concatenated them into one string with a return between the two.
Seems a strange JSON. You could have used like this
{
"categories": [
"unknownCategoryName": [
{
"key1": "value1",
"key2": "value2"
}
],
"unknownCategoryName2": [
{
"key1": "value1",
"key2": "value2"
}
]
]
}
And it is very easy to sort if you did like above.
To sort JSON provided in your question
-(NSMutableArray *)getAllKeys:(NSDictionary *)jsonDictionary{
NSDictionary *all = [results objectForKey:#"categories"];
NSMutableArray *allKeys = [all allKeys];
NSSortDescriptor *sorter = [[NSSortDescriptor alloc] initWithKey:#"self" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject: sorter];
[allKeys sortUsingDescriptors:sortDescriptors];
[sorter release];
return allkeys;
}
-(void)createSortedDictionary{
NSMutableDictionary *dictionaryMutable=[NSMutableDictionary dictionary];
for(NSString *string in allkeys){
[dictionaryMutable setObject:[all objectForKey:string] forKey:string];
}
}
dictionaryMutable will hold the sorted dictionary
Did you write this JSON or are you getting it from somewhere that you can't edit? It looks malformed to me. categories should be an array and the current array placement makes no sense. If the JSON looked like this:
{
categories:[
{unknownCategoryName:
{ key1:value1,
key2:value2
}},
{unknownCategoryName2:
{ key1:value1,
key2:value2
}}
]
}
Then you could read categories into an NSArray, and your delegate methods would look like this (and display in proper order):
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [categoriesArray count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
NSDictionary *dict = [categoriesArray objectAtIndex:section];
NSString *title = [[dict allKeys] objectAtIndex:0];
return title;
}
I think the only way you can preserve the original order is to do the initial parsing yourself after creating a JSON string from the JSON data. If the structure of the JSON string is as you posted, you could use an NSScanner to find the strings between quote marks, and then select only the ones that have a [ after the final quotation mark to add to an array which will be your ordered array of keys. You can then create the NSDictionary using a JSON parser, and get the values by looping through your ordered keys and sending them valueForKey:.

Allocating Right Amount of Rows Based On Array

I have a NSMutableArray filled with NSMutableArray's. I would like to fill my table view up with the appropriate amount of rows, based on the size of a particular indexes array.
I currently have the array setup to grab the first element in the array, and the table then sets the amount of rows to that particular array's size. Ideally, i'd like to set the rows to the count of each element, of which most (arrays) have differing sizes.
Here is what I currently have:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
WorkoutManager *workoutManager = [WorkoutManager sharedInstance];
NSMutableArray *blah = [[workoutManager workouts] objectAtIndex:0];
return [blah count];
}
Any help would be greatly appreciated.
Do it in following way:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
WorkoutManager *workoutManager = [WorkoutManager sharedInstance];
return [[workoutManager workouts] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
WorkoutManager *workoutManager = [WorkoutManager sharedInstance];
NSMutableArray *blah = [[workoutManager workouts] objectAtIndex:indexPath.section];
return [blah count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
WorkoutManager *workoutManager = [WorkoutManager sharedInstance];
NSMutableArray *blah = [[workoutManager workouts] objectAtIndex:indexPath.section];
YourObj *obj = [blah objectAtIndex:indexPath.row];
//Do further...
}

Filling UITableView Cells from an NSMutableArray within a NSMutableDictionary

I have a NSMutableDictionary that contains NSMutableArrays. The dictionary represents divisions, and the arrays represents the departments within that division. I am trying to fill in the cells of a UITableView from the contents of the NSMutableArray. I currently have the UITableView displaying the correct amount of sections (divisions) and the correct number of cells within each division [departmentArray count];
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return [divisionArray count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
NSArray *temp = [divisionDict objectForKey:[divisionArray objectAtIndex:section]];
return [temp count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
NSString *temp = [divisionArray objectAtIndex:section];
if ([temp isEqualToString:#"School of Humanities and Social Sciences"])
{
temp = #"Humanities and Social Sciences";
} else if ([temp isEqualToString:#"School of Science and Mathematics"]) {
temp = #"Science and Mathematics";
} else if ([temp isEqualToString:#"School of Education"]) {
temp = #"Education";
}
return temp;
}
I have tried many different things in cellForRowAtIndexPath to display the names of the departments for each division, but I cannot get it to work. I understand I have to get the array for each key, then go through that array and get the names of each dept, but implementing that in cellForRowAtIndexPath is confusing me.
Any help would be greatly appreciated!
I think you would be better off changing your structure to have an NSMutableArray that contains NSMutableDictionarys that contain NSMutableArrays for the rows and NSStrings for the titles. This I found to be very convenient in some of the code I have been developing.
Here is how is works:
You have an NSMutableArray that has an entry for each division, that ends up being a section in the table view.
Within each entry of the array you have an NSMutableDictionary that contains a two entries, one that I used the key #"rows" for, that contains an array with the rows, and one that I used the key #"title" for, that holds the section header.
Your code then becomes:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return [divisionArray count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
NSArray *rowsInSection = [[divisionArray getObjectAtIndex: section] objectForKey: #"rows"];
return [rowsInSection count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
NSString *rawTitle = [[divisionArray getObjectAtIndex: section] objectForKey: #"title"];
NSString *sectionTitle = [rawTitle stringByReplacingOccurrencesOfString: #"School of " withString: #""];
return sectionTitle;
}
Keeping my structure the same, I was able to figure it out, heres the lines I places in my cellForRowAtIndexPath method:
NSArray *temp = [divisionDict objectForKey:[divisionArray objectAtIndex:[indexPath section]]];
[[cell textLabel] setText:[temp objectAtIndex:[indexPath row]]];