Filling UITableView Cells from an NSMutableArray within a NSMutableDictionary - iphone

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]]];

Related

How to get display data in UITableView Section tables with single Array

I have data on a server which I want to show in the a tableView.
The problem is that I want to show data based on categories so I have array categories which has categories which will be section titles and inside them there data so for display the data in section I have to declare Array.
e.g. If there are three categories then we have to make three array to populate data but what if there are more categories as categories are dynamic and come from server.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [categoryArray count] ;
}
And how to set title for section title, as it is in category array, so if it is section one by one in array.
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
NSLog(#"Number of Sections");
if(section == 0)
return #"Sales";
if(section == 1)
return #"Soft Skills";
}
How to show data in tableView cells may I create arrays for all the categories?
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (section==0)
{
appDelegate = (MultipleDetailViewsWithNavigatorAppDelegate *)[[UIApplication sharedApplication] delegate];
int count=[resultArray count];
NSLog(#"resultArry Row Counts is %d",count);
return [resultArray count];
}
else{
appDelegate = (MultipleDetailViewsWithNavigatorAppDelegate *)[[UIApplication sharedApplication] delegate];
int count=[resultArrayOne count];
NSLog(#"resultArry Row Counts is %d",count);
return [resultArrayOne count];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"Table Cell Data");
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
if (indexPath.section==0) {
appDelegate = (MultipleDetailViewsWithNavigatorAppDelegate *)[[UIApplication sharedApplication] delegate];
ObjectData *theCellData = [resultArray objectAtIndex:indexPath.row];
NSString *cellValue =theCellData.sub_Category;
cell.font=[UIFont fontWithName:#"Helvetical Bold" size:14];
NSLog(#"Cell Values %#",cellValue);
cell.textLabel.text = cellValue;
return cell;
}
else {
appDelegate = (MultipleDetailViewsWithNavigatorAppDelegate *)[[UIApplication sharedApplication] delegate];
ObjectData *theCellData = [resultArrayOne objectAtIndex:indexPath.row];
NSString *cellValue =theCellData.sub_Category;
cell.font=[UIFont fontWithName:#"Helvetical Bold" size:14];
NSLog(#"Cell Values %#",cellValue);
cell.textLabel.text = cellValue;
return cell;
}
}
Since getting the categories from the server does not seem to be your question I base my answer on pre filled arrays for a better visualization.
NSMutableArray *categories = #[#"Cat1", #"Cat2"];
// creata a dictionary with all the array for the categorie rows
NSMutableDictionary *rowDict = #{#"Cat1":#[#"Cat1Row1",#"Cat1Row2",#"..."],
#"Cat2":#[#"Cat2Row1", #"Cat2Row2",#"..."]
Key to this solution is that you use the category string as key for the dictionary.
You can now access the title like this
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
return categories[section];
}
And access your rows like this
- (UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// ...
// create or deque a cell like you normally would do
// now configure the cell
cell.textLable.text = [rowDict[categories[indexPath.section]] objectAtIndex:indexPath.row]
}
use
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
return [categoryArray objectAtindex:section];
}
for section title.
likewise, store the values for each categories in a nsmutable array NSDictionary and display the data for each category in uitableviewcell.
Try the below code. It'll solve your problem :
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
return [categoryArray objectAtIndex:section];
}
EDIT :
First Create a model data class to store data of your categories.
Use this model class to feel your numberOfRowsInSection and cellForRowAtIndexPath delegate function.
Instead of creating different-different array for each category. Store this array in one model class. It'll be easy to handle.

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];
});
});
}

indexPath.row Returns Random Cell TextLabel in didSelectRowAtIndexPath

I have a UITableView populated by a SQLite database. I added Section-based Grouping using the sectionIndexTitlesForTableView delegate method today and now when a Cell is selected, the String for indexPath.row is not the same as the text in the selected Cell.
My Code works like this.
I create an Array that holds the businesses from the SQLite database.
I sort that Array alphabetically.
I create an Array of letters of the Alphabet using only the letters of the Alphabet that businesses in the database begin with.
I use that Array, along with an NSPredicate to provide Grouped Header views which group the businesses by their first letter, alphabetically.
The Selected Row is written to the NSUserDefaults file, and a View Controller is pushed (iPhone), or an Observer is added for that key (iPad).
Unfortunately, since adding the header views, indexPath.row now returns a completely different string to that of the TextLabel of the selected Cell, and so a different Business' information is displayed.
Here are the important blocks of code for the main arrays.
- (void)viewDidLoad
{
// Lots of code...
arrayName = [[NSMutableArray alloc] init];
NameSet = [[NSMutableSet alloc] init];
sortedArray = [arrayName sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
alphabet = [[NSMutableArray alloc] init];
[alphabet addObject:#"{search}"];
for (int i=0; i<[sortedArray count]-1; i++)
{
char alphabetUni = [[sortedArray objectAtIndex:i] characterAtIndex:0];
NSString *uniChar = [NSString stringwithFormat:#"%c", alphabetUni];
if (![alphabet containsObject:uniChar])
{
[alphabet addObject:uniChar];
}
}
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [alphabet count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSInteger rows = 0;
NSString *alpha = [alphabet objectAtIndex:section];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF beginswith[c] %#", alpha];
businesses = [sortedArray filteredArrayUsingPredicate:predicate];
if ([tableView isEqual:self.searchDisplayController.searchResultsTableView]){
rows = [self.searchResults count];
}
else {
rows = [businesses count];
}
return rows;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection (NSInteger)section
{
return [alphabet objectAtIndex:section];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier = #"Cell";
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
NSString *alpha = [alphabet objectAtIndex:indexPath.section];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF beginswith[c] %#", alpha];
businesses = [sortedArray filteredArrayUsingPredicate:predicate];
if ([tableView isEqual:self.searchDisplayController.searchResultsTableView]){
cell.textLabel.text =
[self.searchResults objectAtIndex:indexPath.row];
}
else{
NSString *cellValue = [businesses objectAtIndex:indexPath.row];
cell.textLabel.text = cellValue;
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *selected = nil;
if (tableView == self.tableView)
{
selected = [businesses objectAtIndex:indexPath.row];
}
else if (tableView == searchDis.searchResultsTableView)
{
selected = [filteredData objectAtIndex:indexPath.row];
}
[def setObject:selected forKey:#"NameChoiceDetail"];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone)
{
[self performSegueWithIdentifier:#"NameDetailPush" sender:self];
}
}
// Please excuse my horribly written code. I've only been working with Objective-C for 4 months, and Programming for about 8 months. Any suggestions/optimisations will be duly noted.
Your table view uses sections but your implementation of tableView:didSelectRowAtIndexPath: doesn't evaluate the section of the index path. So the code is missing something.
Furthermore, I find that your use of the businesses variable (it's probably an instance variable) very strange. It is assigned a value in tableView:cellForRowAtIndexPath: but not in tableView:didSelectRowAtIndexPath: even though it is used there. So the outcome if the latter depends on what table cell was displayed last and as a consequence it depends on scrolling user interaction. That might be the reason why the outcome looks rather random.

Static Table View

I am creating a static table view (must be compatible with iOS 4 - so I can't use iOS 5's method).
The way I have it is that I have two sections; the first has one cell, and the second has two cells. I made two arrays, one with the title of the only cell in the first section, and the second with both titles for both cells in the second section. So my dictionary looks like this:
(NSDictionary *) {
First = (
Title1 < --- Array (1 item)
);
Second = (
"Title1", < --- Array (2 items)
Title2
);
}
The issue I have is that I need to return number of rows in a section using tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section. So my question is, how do I retrieve the section from the dictionary using NSInteger section? I would also have to do the same thing in tableView:cellForRowAtIndexPath.
Thank you
If you don't understand how dictionaries work, I'd recommend simplifying the problem. Create one array for each section, then inside your delegate methods use a switch() statement to call [array count] for the row count etc. For the section count you could still use the original dictionary with [[dictionary allKeys] count].
EDIT:
I just saw #fzwo recommends the same thing in two comments
Your best bet is an array of arrays, as has been mentioned. To avoid the complexity with dictionaries, create two NSArray ivars for the table data and the section titles.
// in viewDidLoad
tableData = [NSArray arrayWithObjects:
[NSArray arrayWithObjects:
#"Row one title",
#"Row two title",
nil],
[NSArray arrayWithObjects:
#"Row one title",
#"Row two title",
#"Row three title",
nil],
nil];
sectionTitles = [NSArray arrayWithObjects:
#"Section one title",
#"Section two title",
nil];
// in numberOfSections:
return tableData.count;
// in numberOfRowsInSection:
return [[tableData objectAtIndex:section] count];
// in titleForHeaderInSection:
return [sectionTitles objectAtIndex:section];
// in cellForRowAtIndexPath:
...
cell.textLabel.text = [[tableData objectAtIndex:indexPath.section]
objectAtIndex:indexPath.row];
You could use other objects instead of the row titles if you need more data available to your cell.
To get the number of rows in your section you can use the following:
tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSString *key = [[dictionary allKeys] objectAtIndex: section];
return [[dictionary objectForKey:key] count];
}
And to get the cell value:
tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *key = [[dictionary allKeys] objectAtIndex: indexPath.section];
NSArray *values = [dictionary objectForKey:key];
NSString *value = [values objectAtIndex: indexPath.row];
// code to create a cell
return cell;
}

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...
}