I have been trying for days to figure out how to parse this JSON to a sectioned UITable but I am not successful, I've only been able to figure out how to get the section name, but failed to get each section row count and data for each row in each section.
Since the transportation group may vary from time to time and their name may change, so I guess I need to use allKeys to find out each section title 1st.
Please help and points me to the right direction to extract the data for a sectioned UITable, Thank you.
{
"transport" : {
"public" : [
{
"transport_id" : "2",
"transport_name" : "Ferry"
},
{
"transport_id" : "3",
"transport_name" : "Bus"
},
{
"transport_id" : "4",
"transport_name" : "Taxi"
},
{
"transport_id" : "5",
"transport_name" : "Tram"
}
],
"Private" : [
{
"transport_id" : "11",
"transport_name" : "Bicycle"
},
{
"transport_id" : "12",
"transport_name" : "Private Car"
}
],
"Misc" : [
{
"transport_id" : "6",
"transport_name" : "By Foot"
},
{
"transport_id" : "7",
"transport_name" : "Helicopter"
},
{
"transport_id" : "8",
"transport_name" : "Yatch"
}
]
}
}
NSDictionary *results = [jsonString JSONValue];
NSDictionary *all = [results objectForKey:#"transport"];
NSArray *allKeys = [all allKeys];
NSArray *transports = [results objectForKey:#"transport"];
for (NSDictionary *transport in transports)
{
[transportSectionTitle addObject:(transport)];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [transportSectionTitle count];
}
The easiest solution to explain is to use the all dictionary as your datasource.
NSDictionary *results = [jsonString JSONValue];
NSDictionary *all = [results objectForKey:#"transport"];
// self.datasource would be a NSDictionary retained property
self.datasource = all;
Then to get the number of sections you could do :
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [self.datasource count]; // You can use count on a NSDictionary
}
To get the title of the sections:
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
NSString *title = [[self.datasource allKeys] objectAtIndex:section];
return title;
}
To get the number of rows in each section:
- (NSInteger)tableView:(UITableView *)favTableView numberOfRowsInSection:(NSInteger)section {
// Get the all the transports
NSArray *allTransports = [self.datasource allValues];
// Get the array of transports for the wanted section
NSArray *sectionTransports = [allTransports objectAtIndex:section];
return [sectionTransports count];
}
Then to get the rows :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
// Get the all the transports
NSArray *allTransports = [self.datasource allValues];
// Get the array of transports for the wanted section
NSArray *sectionTransports = [allTransports objectAtIndex:indexPath.section];
// Then get the transport for the row
NSDictionary *transport = [sectionTransports objectAtIndex:indexPath.row];
// Now you can get the name and id of the transport
NSString *tansportName = [transport objectForKey:#"transport_name"];
NSString *transportId = [transport objectForKey:#"transport_id"];
NSString *transportDescription = [NSString stringWithFormat:#"%# - %#",transportId, transportName];
cell.textLabel.text = transportDescription;
return cell;
}
That's the gist of it anyway.
You might want to store the allKeys and allValues arrays as class properties instead of having to go through them in all the tableview's delegate and datasource methods, but you should have all the info to build yuor table now.
Hope this helps :)
The key to what you need to do is recognizing that once your JSON string gets parsed into an object it becomes a series of nested NSArrays and NSDictionarys, and you just need to drilling through the values appropriately
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [[transports allKeys] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [(NSArray*)[transports objectForKey:[[transports allKeys] objectAtIndex:section]] count];
}
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView
{
return [transports allKeys];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// get transport category (e.g."public")
NSString *transportCategory = (NSString*)[[transports allKeys] objectAtIndex:[indexPath section]];
// get transport items belonging to the category
NSArray *items = (NSArray*)[transports objectForKey:transportCategory];
// get transport item for this row
NSDictionary *transportItem = [items objectAtIndex:[indexPath row]];
// extract values of transport item
NSString *transportName = [transportItem objectForKey:#"transport_name"];
NSString *transportID = [transportItem objectForKey:#"transport_id"];
cell.textLabel.text = transportName;
return cell;
}
NSDictionary *results = [jsonString JSONValue];
NSDictionary *allTypes = [results objectForKey:#"transport"];
NSArray *allTransportKeys = [allTypes allKeys];
Number of sections:
NSInteger numberOfSections = [allKeys count];
Number of rows in section:
NSString *key = [allKeys objectAtIndex:section];
NSArray *array = [allTypes objectForKey:key];
NSInteger numberOfRows = [array count];
Data at indexPath:
NSString *key = [allKeys objectAtIndex:indexPath.section];
NSArray *array = [allTypes objectForKey:key];
NSDictionary *itemDict = [array objectAtIndex:indexPath.row];
Then you can extract the data from itemDict.
Related
I want to display Plist to the UITableView .By my below code I can display one key values that is list of states. but i want to display all the values of three tables.
- (id)readPlist:(NSString *)fileName
{
NSData *plistData;
NSString *error;
NSPropertyListFormat format;
id plist;
NSString *localizedPath = [[NSBundle mainBundle] pathForResource:fileName ofType:#"plist"];
plistData = [NSData dataWithContentsOfFile:localizedPath];
plist = [NSPropertyListSerialization propertyListFromData:plistData mutabilityOption:NSPropertyListImmutable format:&format errorDescription:&error];
if (!plist) {
NSLog(#"Error reading plist from file '%s', error = '%s'", [localizedPath UTF8String], [error UTF8String]);
}
return plist;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [[[self readPlist:#"sample"] objectForKey:#"ListOfStates"] count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
cell.textLabel.text = [[[self readPlist:#"sample"] objectForKey:#"ListOfStates"] objectAtIndex:indexPath.row];
return cell;}
MY output comes only listofkeystates values
is it also possible to display key name with values
Maybe use table sections...
-(NSInteger) numberOfSectionsInTableView:(UITableView *)tableView {
NSDictionary* dict = [self readPlist:#"sample"];
return dict.allKeys.count;
}
-(NSString*) tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
NSDictionary* dict = [self readPlist:#"sample"];
return [dict.allKeys objectAtIndex:section];
}
-(NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSDictionary* dict = [self readPlist:#"sample"];
NSString* key = [dict.allKeys objectAtIndex:section];
return [[dict valueForKey:key] count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSDictionary* dict = [self readPlist:#"sample"];
NSString* key = [dict.allKeys objectAtIndex:indexPath.section];
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
cell.textLabel.text = [[dict objectForKey:key] objectAtIndex:indexPath.row];
return cell;
}
just try to get data in NSDictionary and NSArray and then use it in UITableView
NSString *file = [[NSBundle mainBundle] pathForResource:#"sample" ofType:#"plist"];
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:file];
and use tableValue with its name like bellow...
NSArray *array = [dict objectForKey:#"ListOfStates"];
NSLog(#"%#", array);
i hope this help you..
EDIT : Corrected some typo mistakes
The idea
Loading plist in a dictonary
optional : sort it (!)
create the array of data for each section
create the array of section
Then implement the following things
viewDid load -> load the plist file in a dictionary, create an array of the sections and a dictionary of the data
numberOfSectionsInTableView -> the number of sections. In your case the number of keys
numberOfRowsInSection -> the number of rows of each section
titleForHeaderInSection -> for each section, the title of the section
cellForRowAtIndexPath -> the data of all row
sectionIndexTitlesForTableView -> the array that holds all you sections names. In your case all the keys
assuming you have a property called mySections and a property called myData
also assuming you use storyboard with standard cell with recycle identifier "Cell"
#synthesize mySections
#synthesize myData
- (void)viewDidLoad
{
[super viewDidLoad];
//LOADING THE PLIST FILE
//Create a string representing the file path
NSString *plistPath = [bundle pathForResource:#"yourFilename" ofType:#"plist"];
//Load the file in a dictionnary
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:plistPath];
self.myData = dict;
//SORTING THE DICTIONARY
NSArray *dicoArray = [[self.myData allKeys] sortedArrayUsingComparator:^(id firstObject, id secondObject) {
return [((NSString *)firstObject) compare:((NSString *)secondObject) options: NSCaseInsensitiveSearch];
}];
self.mySections = dicoArray;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
//Return the number of sections.
return [self.mySections count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
NSString *key = [self.mySections objectAtIndex:section];
NSArray *dataInSection = [self.myData objectForKey:key];
return [dataInSection count];
}
-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
NSString *key = [self.mySections objectAtIndex:section];
return [NSString stringWithFormat:#"%#", key];
}
-(NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
return self.mySections;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath
{
NSUInteger section = [indexPath section];
NSUInteger row = [indexPath row];
NSString *key = [self.mySections objectAtIndex:section];
NSArray *dataForSection = [self.myData objectForKey:key];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
cell.textLabel.text = [dataForSection objectAtIndex:row];
return cell;
}
I have a main array, that contains a whole bunch of dictionaries, what I want to do is to have all those dictionaries sorted according to their assigned tag. This is how a dictionary might look:
date = "2012-12-04 20:26:04 +0000";
name = H;
tag = "#J";
Heres how the main array looks:
MAIN_ARRAY
- dict1
- dict2
- dict3
I want to sort the main array like this:
MAIN_ARRAY
- tag1
- dict1
- dict2
- tag2
- dict3
Heres my code:
-(NSArray *)returnTagContent {
NSArray *tags = [all valueForKey:#"tag"];
NSMutableArray *adoptTags = [[[NSMutableArray alloc] init] autorelease];
for (NSString *tagQuery in tags) {
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"tag CONTAINS[cd] %#", tagQuery];
NSArray *roughArray = [all filteredArrayUsingPredicate:predicate];
NSArray *tagContent = [[NSSet setWithArray:roughArray] allObjects];
[adoptTags addObject:tagContent];
}
return adoptTags;
}
It returns the array, but now I want to organize it into section headers. How should I go about this?
I also have another piece of code with problem for returning the section header titles:
-(NSString *)returnTitleForTags {
NSString *uniqueTag = nil;
for (NSArray *tagContent in allTags) {
uniqueTag = [[[tagContent valueForKey:#"tag"] allObjects] lastObject];
}
return uniqueTag;
}
Problem? Well, I know it's because of lastObject but any other ideas to retrieve a NSString object of the array.
UPDATE: New code changes.
I update the array to display the sections when clicked by a button so like this:
isTagFilterOn=YES;
[self loadSectionsArray];
[self.tableView reloadData];
Heres the code for cellForRowAtIndexPath:
if (isTagFilterOn==YES) {
NSDictionary *dict = [[sectionsArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];
cell.textLabel.text = [dict valueForKey:#"name"];
cell.detailTextLabel.text = [dict valueForKey:#"date"];
}
else {
NSString *object = all[indexPath.row];
cell.textLabel.text = [object valueForKey:#"name"];
cell.detailTextLabel.text = [object valueForKey:#"tag"];
}
The rest
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
if (isTagFilterOn==YES) {
return [sectionsArray count];
}
else {
return 1;
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (isTagFilterOn==YES) {
return [[sectionsArray objectAtIndex:section] count];
}
return all.count;
}
-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
if (isTagFilterOn==YES) {
NSDictionary *dict = [[sectionsArray objectAtIndex:section] objectAtIndex:0];
return [dict objectForKey:#"tag"];
}
return nil;
}
I think your task becomes easier if you remove duplicate "tags" before you create the array for the table view data source:
// All tags:
NSArray *tags = [mainArray valueForKey:#"tag"];
// Remove duplicates and sort:
tags = [[[NSSet setWithArray:tags] allObjects] sortedArrayUsingSelector:#selector(compare:)];
// Build an "array of arrays (of dictionaries)" as data source:
sectionsArray = [NSMutableArray array];
for (NSString *tag in tags) {
NSPredicate *pred = [NSPredicate predicateWithFormat:#"tag == %#", tag];
NSArray *onesection = [mainArray filteredArrayUsingPredicate:pred];
[sectionsArray addObject:onesection];
}
For example, if the mainArray is
(
{ date = "2012-12-04 20:26:04 +0000"; name = H; tag = "#J"; },
{ date = "2013-12-04 20:26:04 +0000"; name = X; tag = "#J"; },
{ date = "2014-12-04 20:26:04 +0000"; name = Z; tag = "#L"; }
)
then sectionsArray will be
(
(
{ date = "2012-12-04 20:26:04 +0000"; name = H; tag = "#J"; },
{ date = "2013-12-04 20:26:04 +0000"; name = X; tag = "#J"; }
),
(
{ date = "2014-12-04 20:26:04 +0000"; name = Z; tag = "#L"; }
)
)
and you can easily access each section and each row within a section:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [sectionsArray count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [[sectionsArray objectAtIndex:section] count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = ...;
}
NSDictionary *dict = [[sectionsArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];
cell.textLabel.text = [dict objectForKey:#"name"];
cell.detailTextLabel.text = [dict objectForKey:#"date"];
return cell;
}
-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
NSDictionary *dict = [[sectionsArray objectAtIndex:section] objectAtIndex:0];
return [dict objectForKey:#"tag"];
}
I have a JSON file, and i need to extract values from it and display on my tableview. Everything works fine.
{
"1": {
"name": "Jemmy",
"birthday": "1994-11-23"
},
"2": {
"name": "Sarah",
"birthday": "1994-04-12"
},
"3": {
"name": "Deb",
"birthday": "1994-11-23"
},
"4": {
"name": "Sam",
"birthday": "1994-11-23"
}
}
When i display the values, it doesn't get displayed in the order of 1,2,3,4 as given in the records. It just gets displayed randomly. I have included my code below, I need it to be modified so i could display the content in the order given above. Can someone please help ?
- (void)requestSuccessfullyCompleted:(ASIHTTPRequest *)request{
NSString *responseString = [request responseString];
SBJsonParser *parser = [SBJsonParser new];
id content = [responseString JSONValue];
if(!content){
return;
}
NSDictionary *personDictionary = content;
NSMutableArray *personMutableArray = [[NSMutableArray alloc] init];
for (NSDictionary *childDictionary in personDictionary.allValues)
{
Deal *deal = [[Deal alloc] init];
deal.name=[childDictionary objectForKey:#"name"];
deal.dob=[childDictionary objectForKey:#"birthday"];
[personMutableArray addObject:deal];
}
self.personArray = [NSArray arrayWithArray:personMutableArray];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
Cell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
Person *person = [self.personArray objectAtIndex:indexPath.row];
cell = [[Cell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
cell.namelabel.text=person.name;
cell.doblabel.text=person.dob;
}
return cell;
}
You're not reusing your cell correctly. If the cell is being reused, you fail to configure it.
Your cellForRowAtIndexPath: should be this way:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
Cell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[Cell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}
Person *person = [self.personArray objectAtIndex:indexPath.row];
cell.namelabel.text=person.name;
cell.doblabel.text=person.dob;
return cell;
}
Note that this is correct ARC code, but in pre-ARC, you need to add autorelease to the end of the [Cell alloc] line.
Try this and also correct your cellForRowAtIndexPath: for reused cells
- (void)requestSuccessfullyCompleted:(ASIHTTPRequest *)request{
NSString *responseString = [request responseString];
SBJsonParser *parser = [SBJsonParser new];
id content = [responseString JSONValue];
if(!content){
return;
}
NSDictionary *personDictionary = content;
NSMutableArray *personMutableArray = [[NSMutableArray alloc] init];
NSArray *array = [personDictionary allKeys];
NSArray * sortedArray = [array sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)];
for (NSString *str in sortedArray)
{
NSDictionary *childDictionary = [personDictionary objectForKey:str];
Deal *deal = [[Deal alloc] init];
deal.name=[childDictionary objectForKey:#"name"];
deal.dob=[childDictionary objectForKey:#"birthday"];
[personMutableArray addObject:deal];
}
self.personArray = [NSArray arrayWithArray:personMutableArray];
}
You store the data from the JSON in a dictionary which doesn't store ordered data. You would have to get that data in order in an array or sort the array afterwords.
You could sort the array in which you store the dictionary data, if you wish to sort it alphabetically by name you can do this:
self.personArray = [personMutableArray sortedArrayUsingSelector:#selector(compare:)];
- (NSComparisonResult)compare:(Deal *)dealObject {
return [self.name compare:dealObject.name];
}
If you want the data to be represented just as the JSON you should do something like this:
for (int i = 1; i < [personDictionary.allValues count]; i++)
{
NSDictionary *childDictionary = [[personDictionary objectForKey:[NSString stringWithFormat:#"%d", i]];
Deal *deal = [[Deal alloc] init];
deal.name=[childDictionary objectForKey:#"name"];
deal.dob=[childDictionary objectForKey:#"birthday"];
[personMutableArray addObject:deal];
}
I have the following code to make a table from string turned into a dictionary:
- (void)viewDidLoad {
[super viewDidLoad];
testArray = [[NSArray alloc] init];
NSString *testString = #"Sam|26,Hannah|22,Adam|30,Carlie|32";
testArray = [testString componentsSeparatedByString:#","];
dict = [NSMutableDictionary dictionary];
for (NSString *s in testArray) {
testArray2 = [s componentsSeparatedByString:#"|"];
[dict setObject:[testArray2 objectAtIndex:1] forKey:[testArray2 objectAtIndex:0]];
}
}
- (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] autorelease];
}
// Configure the cell.
if (testArray.count >indexPath.row) {
cell.textLabel.text = [[dict allKeys] objectAtIndex:[indexPath row]];
cell.detailTextLabel.text = [dict objectForKey:cell.textLabel.text];
}
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
return cell;
}
What I would like is for the selected row title to be set as the title on my detail view.
I tried with:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
self.detailController.title = [[dict allKeys] objectAtIndex:[indexPath row]];
[self.navigationController pushViewController:self.detailController animated:YES];
}
But I get an "EXC_BAD_ACCESS" error.
It works fine if I use #"1" as title, it's just something with my dictionary call that's wrong, I assume.
Make dict a retained dictionary instead of an autoreleased one.
I.E. declare it maybe like this:
dict = [[NSMutableDictionary alloc] initWithCapacity: [testArray count]];
in your viewDidLoad method. Make sure to release it when viewDidUnload is called.
Also, make sure of the number of keys in your dict before calling:
self.detailController.title = [[dict allKeys] objectAtIndex:[indexPath row]];
So, I would do:
if(dict && ([[dict allKeys] count] > [indexPath row])
{
self.detailController.title =
[[dict allKeys] objectAtIndex:[indexPath row]];
} else {
self.detailController.title = #"Here's a problem";
}
Did you implement these UITableView delegate methods ? All these are needed. Also can you post more detailed StackTrace.
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section
{
return [resultSet count];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (CGFloat)tableView:(UITableView *)tableView
heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 280.0;
}
Thank you for looking into my question here. I am trying to get uitableview sectioned by dates in descending order. I get the data from the sqllite order by datefield desc. But whatever I do the dates are displayed in ascending order. I have the following set of data that comes out of the db and in this order:
ID BookName DateRead
1 ABC 19-10-2011
2 ABZ 27-06-2011
3 ABD 28-05-2011
I would like the data to appear like the following
19-10-2011
ABC
27-06-2011
ABZ
28-05-2011
ABD
but no matter what I am trying I am getting the data returned as below:
19-10-2011
ABC
28-05-2011
ABZ
27-06-2011
ABD
Here is the complete list of code that I am using:
.h file
#import <UIKit/UIKit.h>
#interface BookHistoryViewController : UITableViewController {
NSArray *books;
NSMutableDictionary *sections;
}
#property (nonatomic,retain) NSArray *books;
#property (nonatomic,retain) NSMutableDictionary *sections;
#end
Here is my .m file
- (void)viewDidLoad {
TestAppDelegate *appDelegate = (TestAppDelegate *)[[UIApplication sharedApplication] delegate];
[Book getInitialDataToDisplay:[appDelegate getDBPath]];
self.books = [NSMutableArray arrayWithArray:appDelegate.bookArray];
self.sections = [[NSMutableDictionary alloc] init];
BOOL found;
// Loop through the books and create our keys
for (NSDictionary *book in books)
{
NSString *c = [book valueForKey:#"DateRead"];
found = NO;
for (NSString *str in [self.sections allKeys])
{
if ([str isEqualToString:c])
{
found = YES;
}
}
if (!found)
{
[self.sections setValue:[[NSMutableArray alloc] init] forKey:c];
}
}
// Loop again and sort the books into their respective keys
for (NSDictionary *book in self.books)
{
[[self.sections valueForKey:[book valueForKey:#"DateRead"]] addObject:book];
}
// Sort each section array
for (NSString *key in [self.sections allKeys])
{
[[self.sections objectForKey:key] sortUsingDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"DateRead" ascending:NO]]];
}
[super viewDidLoad];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [[self.sections allKeys] count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
return [[[self.sections allKeys] sortedArrayUsingSelector:#selector(compare:)] objectAtIndex:section];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [[self.sections valueForKey:[[[self.sections allKeys] sortedArrayUsingSelector:#selector(compare:)] 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] autorelease];
}
NSDictionary *book = [[self.sections valueForKey:[[[self.sections allKeys] sortedArrayUsingSelector:#selector(compare:)] objectAtIndex:indexPath.section]] objectAtIndex:indexPath.row];
cell.textLabel.text = [book valueForKey:#"title"];
return cell;
}
When you get the book in tableView:cellForRowAtIndexPath:, you first sort by section key which is the dateRead as a string, but you sort in ascending order. You probably want to sort using a descriptor like you do in viewDidLoad. In fact, why not just keep it in the correctly sorted order so that you don't have to sort each time you need a cell? The way you are doing it will cause the interface to grind to a halt pretty quickly.