Add Alphabetical Headers to UITableView with a sorted array - ios

I'm sorting my array by last name alphabetically. I'd like to separate this into sections with the appropriate header above each section (A, B, C, etc.).
Here's what I've tried below:
// Here is where I refresh the data and sort it based on last name
- (void)refreshData {
[[PCMSSessionManager sharedSession] refreshPCMSDataWithCompletion:^(BOOL success, NSString *errorMessage, id resultObject) {
if (success) {
NSLog(#"yay!");
self.membersArray = [[PCMSSessionManager sharedSession] memberArr];
// Let's sort the array
self.sortedArray = [self.membersArray sortedArrayUsingComparator:^NSComparisonResult(id a, id b) {
NSString *first = [(PCMSMember*)a lastName];
NSString *second = [(PCMSMember*)b lastName];
return [first compare:second];
}];
[self.tableView reloadData];
} else {
NSLog(#"boooo!!!!");
}
}];
}
- (NSDictionary *)indexedMembers
{
NSMutableDictionary *indexedContacts = [NSMutableDictionary new];
for (PCMSMember *member in self.sortedArray)
{
NSString *sortString = member.lastName;
NSString *sortLetter = [sortString substringToIndex:1];
/* see if that letter already exists as an index */
BOOL foundKey = NO;
for (NSString *key in [indexedContacts allKeys])
{
if ([key isEqualToString:sortLetter])
{
foundKey = YES;
}
}
NSMutableArray *valueArray;
if (foundKey)
{
valueArray = [((NSArray *)indexedContacts[sortLetter]) mutableCopy];
}
else
{
valueArray = [NSMutableArray new];
}
[valueArray addObject:member];
indexedContacts[sortLetter] = [valueArray copy];
}
return [indexedContacts copy];
}
// Here's my table data
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [[[self indexedMembers] allKeys] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSDictionary *indexedContacts = [self indexedMembers];
NSArray *myKeys = [indexedContacts allKeys];
NSString *key = myKeys[section];
return [((NSArray *)[self indexedMembers][key]) count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"cellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
// Configure the cell...
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
if (self.isPhysician == YES) {
NSString *key = [[self indexedMembers] allKeys][indexPath.section];
PCMSMember *currentMember = ((NSArray *)[self indexedMembers][key])[indexPath.row];
cell.textLabel.text = [NSString stringWithFormat:#"%# %#", currentMember.firstName, currentMember.lastName];
}
return cell;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return [[self indexedMembers] allKeys][section];
}
UPDATE:
This is getting me closer to what I want.
The data is loading, it's being grouped properly and the headers are showing.
But it's not in alphabetical order.
How can I improve this code to show alphabetically?
It's showing in alphabetical order in my console, just not in the app.

The NSMutableDictionary is unordered by definition. It is not the natural choice if you rely on the order of the stored objects. I suggest you to use NSMutableArray instead. To store the tableview data for each section you can use this mini class
#interface MembersWithSameInitial : NSObject
#property (strong) NSString* initial;
#property (strong) NSMutableArray<PCMSMember*>* members;
#end
#implementation MembersWithSameInitial
#end
After you have sorted the members, all the data for the tableview can be produced with this before tableView reload.
NSMutableArray<MembersWithSameInitial*>* groupedMembers = [[NSMutableArray alloc] init];
for (PCMSMember* member in sortedArray) {
NSString* inicial = [member.lastName substringToIndex:1];
MembersWithSameInitial* last = [groupedMembers lastObject];
if (last && [last.initial isEqualToString:inicial]) {
[last.members addObject:member];
} else {
MembersWithSameInitial* newGroup = [[MembersWithSameInitial alloc] init];
newGroup.initial = inicial;
newGroup.members = [[NSMutableArray alloc] initWithObjects:member, nil];
[groupedMembers addObject:newGroup];
}
}
Since the structure of groupedMembers fits to a grouped tableView, the dataSource methods will have trivial implementations. Assuming, that you have stored groupedMembers in a property.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return self.groupedMembers.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.groupedMembers[section].members.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//...
PCMSMember *currentMember = self.groupedMembers[indexPath.section].members[indexPath.row];
//...
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return groupedMembers[section].initial;
}

Suggestion:
Create two properties
#property NSMutableArray *keys; // for the letters in alphabetical order
#property NSMutableDictionary *indexedContacts; // same as your implementation.
In the method refreshData call the method to create the data source and then reload the table view on the main thread.
Actually you don't need the properties memberArray and sortedArray anymore. The sorted array is passed to the method to create the data source.
- (void)refreshData {
[[PCMSSessionManager sharedSession] refreshPCMSDataWithCompletion:^(BOOL success, NSString *errorMessage, id resultObject) {
if (success) {
NSLog(#"yay!");
self.membersArray = [[PCMSSessionManager sharedSession] memberArr];
// Let's sort the array
NSArray *sortedArray = [self.membersArray sortedArrayUsingComparator:^NSComparisonResult(id a, id b) {
NSString *first = [(PCMSMember*)a lastName];
NSString *second = [(PCMSMember*)b lastName];
return [first compare:second];
}];
[self indexMembers:sortedArray];
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
} else {
NSLog(#"boooo!!!!");
}
}];
}
The method indexMembers initializes the properties keys and indexedContacts and creates the data source.
- (void)indexMembers:(NSArray *)sortedMembers
{
self.keys = [[NSMutableArray alloc] init];
self.indexedContacts = [[NSMutableDictionary alloc] init];
for (PCMSMember *member in sortedMembers)
{
NSString *sortString = member.lastName;
NSString *sortLetter = [sortString substringToIndex:1];
/* see if that letter already exists as an index */
NSArray *keyArray = self.indexedContacts[sortLetter];
NSMutableArray *valueArray;
if (keyArray) {
// array for key exists, use it
valueArray = [keyArray mutableCopy];
} else {
// array for key does not exist, create a new one
valueArray = [NSMutableArray new];
// and add the letter to keys
[self.keys addObject:sortLetter];
}
[valueArray addObject:member];
self.indexedContacts[sortLetter] = [valueArray copy];
}
}
numberOfSectionsInTableView returns the number of keys
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return self.keys.count;
}
numberOfRowsInSection gets the appropriate array for the given section and returns the number of items.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSString *letter = self.keys[section];
NSArray *memberArray = self.indexedContacts[letter];
return memberArray.count;
}
In cellForRowAtIndexPath use the method dequeueReusableCellWithIdentifier: forIndexPath: to get always a valid cell. Then like in numberOfRowsInSection get the actual member array and populate the label.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"cellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
// Configure the cell...
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
if (self.isPhysician == YES) {
NSString *letter = self.keys[indexPath.section];
NSArray *memberArray = self.indexedContacts[letter];
PCMSMember *currentMember = memberArray[indexPath.row];
cell.textLabel.text = [NSString stringWithFormat:#"%# %#", currentMember.firstName, currentMember.lastName];
}
return cell;
}
titleForHeaderInSection simply returns the letter for the section
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return self.keys[section];
}
You're calling indexedMembers too much. This is very expensive.
I couldn't test the code, maybe there is a self or something else missing but you get an impression of the workflow.

Related

How to make up an array with objects for title index uitableview iOS?

I'm making a title indexed event invitation list. I have an array with EventStatus objects. I'm making the _data array for table like,
for (int i = 0; i < eventStatusList.count; i++) {
NSString *firstName = ((OCEventStatus*)eventStatusList[i]).user.firstName;
[_sortedUsers setObject:((OCEventStatus*)eventStatusList[i]).user.firstName forKey:[firstName substringToIndex:1]];
}
_sortedUserTitles = [[[_sortedUsers allKeys] sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)] mutableCopy];
_data = [eventStatusList mutableCopy];
[self.dataTableView reloadData];
and I think this for loop thing is too slow. Is there a way to do this in a good manner? Following is the title index making up logic with UITablrViewDataSource methods.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return [_sortedUserTitles count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
NSString *sectionTitle = [_sortedUserTitles objectAtIndex:section];
NSArray *sectionUsers = [_sortedUsers objectForKey:sectionTitle];
return [sectionUsers count];
}
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView
{
// return animalSectionTitles;
return _alphabetArray;
}
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index
{
return [_sortedUserTitles indexOfObject:title];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
return [_sortedUserTitles objectAtIndex:section];
}
This is error because of in the _sortedUsers dictionary has a string instead of an array. How may I fix this? And also please suggest a fast, good manner to implement this title index.
If you want to create an List of Firstnames try this perhaps.
At interface:
#property(nonatomic, strong)NSMutableDictionary *dict;
#property(nonatomic, strong)NSMutableArray *alphabet;
_dict = [NSMutableDictionary new];
_alphabet = [NSMutableArray new];
[eventStatusList sortUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) {
OCEventStatus *ev1 = (OCEventStatus*)obj1;
OCEventStatus *ev2 = (OCEventStatus*)obj2;
return [ev1.firstName compare:ev2.firstName];
}];
for(OCEventStatus *state in eventStatusList){
NSString *firstchar = [[state.firstName substringToIndex:1] lowercaseString];
if([dict objectForKey:firstchar]==nil){
NSMutableArray *tmp = [NSMutableArray new];
[tmp addObject:state];
[_dict setObject:tmp forKey:firstchar];
[_alphabet addObject:firstChar];
}else{
[[dict objectForKey:firstchar] addObject:state];
}
}
Now you have an Array of firstnames in a Dictionary which has the first letter as the key for Example: a -> ["Alfred","Albert",...]
In the Datasource methods you have to return it like this...
-(NSInteger)numberOfSectionsInTableView:(UITableView*)tableView{
return dict.count;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection;(NSInteger)section
{
return [dict objectForKey:[alphabet objectAtIndex:section]].count;
}
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
OVEventStatus *state = [[dict objectForKey:[alphabet objectAtIndex:indexPath.section]] objectAtIndex:indexPath.row];
cell.textLabel.text = state.firstName;
return cell;
}
Please try if this fits for you
Update:
If you also want to Sort the Arrays I would recommend to sort the eventListBy firstName first! So you have the correct order when you loop over the eventStatusList.

How to group UITableView with section mentioned in JSON value

I get this json from web service and I need to group it depending on "PRICELISTCATEGORY" value. I tried the following code, but I get repeated rows and sections in the table. I collect the web service array in self.arrayPriceList. What am I doing wrong?
After collecting the array from json web service, I call [self didReceiveResponseJson:self.arrayPriceList];
-(NSMutableDictionary *)priceListCategoryDitionaryAllReadyExist:(NSString *)price {
for(NSMutableDictionary *priceListDict in self.arrayPriceList){
if([[[priceListDict objectForKey:#"PRICELISTCATEGORY"] objectForKey:#"text"] isEqualToString:price])
//return the existing array refrence to add
return priceListDict;
}
// if we dont found then we will come here and return nil
return nil;
}
-(void)didReceiveResponseJson:(NSMutableArray *)jsonArray {
for(NSDictionary *priceDict in jsonArray) {
NSMutableDictionary *existingPriceListDict=[self priceListCategoryDitionaryAllReadyExist:[[priceDict objectForKey:#"PRICELISTCATEGORY"] objectForKey:#"text"]];
NSMutableArray *existingTempArray = [NSMutableArray array];
if(existingPriceListDict != nil) {
//if name exist add in existing array....
[existingTempArray addObject:priceDict];
}
else {
// create new price list array
NSMutableArray *newPriceListArray=[[NSMutableArray alloc] init];
// Add name dictionary in it
[newPriceListArray addObject:priceDict];
// add this newly created pricelist array in globalNameArray
[self.arrayPriceList addObject:newPriceListArray];
}
}
//so at the end print global array you will get dynamic array with the there respetive dict.
//NSLog(#"Table array %#", self.arrayPriceList);
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
TGAPriceListCell *cell = [tableView dequeueReusableCellWithIdentifier:#"TGAPriceListCellId" forIndexPath:indexPath];
NSDictionary *dict;
if (self.isFiltered) {
dict = [self.arrayFilteredPriceList objectAtIndex:indexPath.row];
} else {
dict = [self.arrayPriceList objectAtIndex:indexPath.section];
}
cell.lblAPNBarCode.text = [[dict objectForKey:#"APNBARCODE"] objectForKey:#"text"];
cell.lblAvgCost.text = [[dict objectForKey:#"AVERAGECOST"] objectForKey:#"text"];
return cell;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (self.isFiltered) {
return self.arrayFilteredPriceList.count;
} else {
NSArray *arrayPrice = [self.arrayPriceList objectAtIndex:section];
return [arrayPrice count];
}
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [self.arrayPriceList count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
NSDictionary *arrayPrice = [self.arrayPriceList objectAtIndex:section];
if([arrayPrice count]) {
return [[arrayPrice objectForKey:#"PRICELISTCATEGORY"] objectForKey:#"text"];
}
else
return nil;
}
After calling didReceiveResponseJson in viewDidLoad
self.arrayPriceList = [NSMutableArray array];
self.dictPriceList = [NSMutableDictionary dictionary];
I made changes in tableview datasource methods
-(void)didReceiveResponseJson:(NSMutableArray *)jsonArray {
for (NSDictionary *dict in jsonArray ) {
NSString *strPriceListCategory = [[dict objectForKey:#"PRICELISTCATEGORY"] objectForKey:#"text"];
if ([[self.dictPriceList allKeys] containsObject:strPriceListCategory]) {
NSMutableArray *arrayTemp = [self.dictPriceList objectForKey:strPriceListCategory];
[arrayTemp addObject:dict];
[self.dictPriceList setObject:arrayTemp forKey:strPriceListCategory];
} else {
NSMutableArray *arrayTemp = [[NSMutableArray alloc] initWithObjects:dict, nil];
[self.dictPriceList setObject:arrayTemp forKey:strPriceListCategory];
}
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
TGAPriceListCell *cell = [tableView dequeueReusableCellWithIdentifier:#"TGAPriceListCellId" forIndexPath:indexPath];
NSDictionary *dict;
if (self.isFiltered) {
dict = [self.arrayFilteredPriceList objectAtIndex:indexPath.row];
} else {
NSArray *arrayPriceListAllKeys = [self.dictPriceList allKeys];
NSArray *arrayPrice = [self.dictPriceList objectForKey:[arrayPriceListAllKeys objectAtIndex:indexPath.section]];
dict = [arrayPrice objectAtIndex:indexPath.row];
}
cell.lblAPNBarCode.text = [[dict objectForKey:#"APNBARCODE"] objectForKey:#"text"];
cell.lblAvgCost.text = [[dict objectForKey:#"AVERAGECOST"] objectForKey:#"text"];
return cell;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (self.isFiltered) {
return self.arrayFilteredPriceList.count;
} else {
NSArray *arrayPriceListAllKeys = [self.dictPriceList allKeys];
NSArray *arrayPrice = [self.dictPriceList objectForKey:[arrayPriceListAllKeys objectAtIndex:section]];
return [arrayPrice count];
}
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [[self.dictPriceList allKeys] count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
NSString *price = [[self.dictPriceList allKeys] objectAtIndex:section];
return price;
}

How can I load tableView from an NSDictionary in IOS? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I had an application in which I had a response like:
{
"16-06-2016" = (
{
cccc = 16;
dddd = 17;
}
);
"17-06-2016" = (
{
cccc = 14;
dddd = 19;
},
{
cccc = 1;
dddd = 9;
}
);
"18-06-2016" = (
{
cccc = 14;
dddd = 19;
},
{
cccc = 1;
dddd = 9;
}
);
}
How will I display this NSDictionary in my UITableView. Can anybody help me?
Try something like this
self.sectionArr = [jsonDic allKeys]; //First get all section from dictionary like this
Now implement TableView delegate methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return self.sectionArr.count;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return [self.sectionArr objectAtIndex:section];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSArray *arr = [jsonDic objectForKey:[self.sectionArr objectAtIndex:section]];
return arr.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell= [tableview dequeueReusableCellWithIdentifier:#"Cell"];
NSArray *arr = [jsonDic objectForKey:[self.sectionArr objectAtIndex:indexPath.section]];
cell.textLabel.text = [[arr objectAtIndex:indexPath.row] valueForKey:#"dddd"];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSArray *arr = [jsonDic objectForKey:[self.sectionArr objectAtIndex:indexPath.section]];
NSLog(#"Selected Obj - %#",[[arr objectAtIndex:indexPath.row] valueForKey:#"dddd"]);
}
Hope this will help you.
When I display an NSDictionary in a UITableView, since delegate methods like cellForRowAtIndexPath uses indexes, I typically do something like this to get an item at a particular index:
-(UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// Get array of keys
NSArray *keys = [self.dictionary allKeys];
// Get key for the current row
NString *key = [keys objectAtIndex:indexPath.row];
// Get object for current row
id object = [self.dictionary objectForKey:key];
// Then do the typical stuff
UITableViewCell *cell = [tableview dequeueReusableCellWithIdentifier:#"Cell"
forIndexPath:indexPath];
....
Though, in your case, "object" is an NSDictionary, so do something like this:
...
// Get object for current row
NSDictionary *item = [self.dictionary objectForKey:key];
id cccc = [item objectForKey:#"cccc"];
id dddd = [item objectForKey:#"dddd"];
....
If you need the keys sorted, then use keysSortedByValueUsingSelector: rather than allKeys.
Maybe something like this?
(I put data you've provided into dict variable)
#interface MainViewController () {
NSDictionary *dict;
NSArray *sortedKeysAsSectionNames;
}
- (void)viewDidLoad {
[super viewDidLoad];
dict = #{#"16-06-2016": #[#{#"cccc": #(16), #"dddd": #(17)}],
#"17-06-2016": #[#{#"cccc": #(14), #"dddd": #(19)},
#{#"cccc": #(1), #"dddd": #(9)}],
#"18-06-2016": #[#{#"cccc": #(14), #"dddd": #(19)},
#{#"cccc": #(1), #"dddd": #(9)}]
};
NSArray *dictKeys = [dict allKeys];
sortedKeysAsSectionNames = [dictKeys sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) {
NSDateFormatter *format = [[NSDateFormatter alloc] init];
format.dateFormat = #"dd-MM-yyyy";
NSDate *date1 = [format dateFromString:(NSString *)obj1];
NSDate *date2 = [format dateFromString:(NSString *)obj2];
return [date1 compare:date2];
}];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return sortedKeysAsSectionNames.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [(NSArray *)[dict objectForKey:sortedKeysAsSectionNames[section]] count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
id key = sortedKeysAsSectionNames[indexPath.section];
NSArray *dataInSectionArray = [dict objectForKey:key];
NSDictionary *dataForCellDict = [dataInSectionArray objectAtIndex:indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"cell"];
}
cell.textLabel.text = [[dataForCellDict objectForKey:#"cccc"] description];
cell.detailTextLabel.text = [[dataForCellDict objectForKey:#"dddd"] description];
return cell;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return sortedKeysAsSectionNames[section];
}

Deleting tableview row and section if empty with commitEdittingStyle

I can't seem to find too much on this. I'm currently trying to create a swipable delete button that will delete the row that is swiped, and if that row is now empty from the section header it will delete the section header as well. For example, "Bread" is swiped to delete, and there is nothing else under the section header "B". Then this will delete both Bread, and the "B" section header. My code is below.
#interface ChoicesTableViewController () <UITableViewDelegate, UITableViewDataSource>
#property (weak, nonatomic) IBOutlet UITableView *myTableView;
#property (strong, nonatomic) NSMutableArray *items;
#property (strong, nonatomic) NSMutableDictionary *alphabetizedItems;
#end
#implementation ChoicesTableViewController
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.myTableView.delegate = self;
self.myTableView.dataSource = self;
self.items = [[NSMutableArray alloc] init];
[self.items addObject:#"Apples"];
[self.items addObject:#"Bread"];
self.alphabetizedItems = [self alphabetizeItems:self.items];
}
//Segue if the item is tapped
//- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
//{
// MyDataChoices *currentRow = self.arrayNames[indexPath.row];
// self.mySelectedCell = currentRow.myNameChoices;
//
// [self performSegueWithIdentifier:#"unwindSegueAction" sender:self];
//
//}
////unwind segue from add choice
- (IBAction)unwindSegueToChoices:(UIStoryboardSegue *)segue
{
AddChoiceViewController *sourceVC = segue.sourceViewController;
NSString *myNewItem = sourceVC.myTextField.text;
//NSString *myFinalString = [[myNewItem substringToIndex:1] capitalizedString];
NSString *stringCapitalized = [myNewItem capitalizedString];
[self.items addObject:stringCapitalized];
self.alphabetizedItems = [self alphabetizeItems:self.items];
//[self.arrayNames addObjectsFromArray:#[[MyDataChoices itemWithNewName:stringCapitalized]]];
[self.tableView reloadData];
}
//titles for talble view
#pragma mark Helper Methods
- (NSMutableDictionary *)alphabetizeItems:(NSArray *)items {
NSMutableDictionary *buffer = [[NSMutableDictionary alloc] init];
// Put Fruits in Sections
for (int i = 0; i < [items count]; i++) {
NSString *fruit = [items objectAtIndex:i];
NSString *firstLetter = [[fruit substringToIndex:1] uppercaseString];
if ([buffer objectForKey:firstLetter]) {
[(NSMutableArray *)[buffer objectForKey:firstLetter] addObject:fruit];
} else {
NSMutableArray *mutableArray = [[NSMutableArray alloc] initWithObjects:fruit, nil];
[buffer setObject:mutableArray forKey:firstLetter];
}
}
// Sort Fruits
NSArray *keys = [buffer allKeys];
for (int j = 0; j < [keys count]; j++) {
NSString *key = [keys objectAtIndex:j];
[(NSMutableArray *)[buffer objectForKey:key] sortUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
}
NSMutableDictionary *result = [NSMutableDictionary dictionaryWithDictionary:buffer];
return result;
}
#pragma mark title indexing
-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
NSArray *keys = [[self.alphabetizedItems allKeys] sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
NSString *key = [keys objectAtIndex:section];
return key;
}
# pragma mark main table view
-(NSInteger) numberOfSectionsInTableView:(UITableView *) tableView
{
NSArray *keys = [self.alphabetizedItems allKeys];
return [keys count];
}
-(NSInteger) tableView:(UITableView *) tableView numberOfRowsInSection:(NSInteger)section
{
//return self.arrayNames.count;
NSArray *unsortedKeys = [self.alphabetizedItems allKeys];
NSArray *sortedKeys = [unsortedKeys sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
NSString *key = [sortedKeys objectAtIndex:section];
NSArray *fruitsForSection = [self.alphabetizedItems objectForKey:key];
return [fruitsForSection count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//MyDataChoices *currentRow = self.arrayNames[indexPath.row];
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:#"mainCell2" forIndexPath:indexPath];
//cell.textLabel.text = currentRow.myNameChoices;
NSArray *unsortedKeys = [self.alphabetizedItems allKeys];
NSArray *sortedKeys = [unsortedKeys sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
NSString *key = [sortedKeys objectAtIndex:[indexPath section]];
NSArray *fruitsForSection = [self.alphabetizedItems objectForKey:key];
NSString *fruit = [fruitsForSection objectAtIndex:[indexPath row]];
[cell.textLabel setText:fruit];
return cell;
}
# pragma Mark delete slide button
//Delete Swipe Button
// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the specified item to be editable.
return YES;
}
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
int index = indexPath.row;
//[self.items removeObjectAtIndex:index];
[self.alphabetizedItems removeObjectForKey:indexPath];
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
} else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
The basic approach is to see how many rows are in the section for the row being deleted. If the section has two or more rows, simply delete the row as you are doing now. If the section only has one row (the one being deleted), then remove the section from the data model and then delete the section from the table instead of deleting the row.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
NSArray *unsortedKeys = [self.alphabetizedItems allKeys];
NSArray *sortedKeys = [unsortedKeys sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
NSString *key = [sortedKeys objectAtIndex:[indexPath section]];
NSArray *fruitsForSection = [self.alphabetizedItems objectForKey:key];
if (fruitsForSection.count == 1) {
// Delete the whole section
[self.alphabetizedItems removeObjectForKey:key];
[tableView deleteSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationFade];
} else {
// Delete the row from the data source
NSInteger index = indexPath.row;
//[self.items removeObjectAtIndex:index];
[self.alphabetizedItems removeObjectForKey:indexPath];
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
} else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}

Adding additional data to inner array with sorted outer array

I have a plist with contacts: the root is an array, items 0-150 are dictionaries, each dictionary is a single contact with a "name", "number", and "email" string.
The code below sorts the contacts alphabetically into sections based upon the "name" string. Then uses the inner array to populate the cells for each section. I then pass the name from the inner array to my detail view.
However, I can not figure out how to pass the correct number and email for each contact into the detail view. I've been working on this issue for a long while and can not find a solution.
#interface ContactsViewController ()
-(void)configureSectionData;
#end
#implementation ContactsViewController
#synthesize tableData;
#synthesize collation;
#synthesize outerArray;
#synthesize indexTitlesArray, namesDictionary;
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - Table view methods
-(void)configureSectionData {
NSUInteger sectionTitlesCount = [collation.sectionTitles count];
self.outerArray = [NSMutableArray arrayWithCapacity:sectionTitlesCount];
for (NSUInteger index = 0; index < sectionTitlesCount; index++) {
NSMutableArray *array = [NSMutableArray array];
[self.outerArray addObject:array];
}
for (NSString *nameString in tableData)
{
NSInteger sectionNumber = [collation sectionForObject:nameString collationStringSelector:#selector(lowercaseString)];
NSMutableArray *sectionNames = [outerArray objectAtIndex:sectionNumber];
[sectionNames addObject:nameString];
}
for (NSUInteger index = 0; index < sectionTitlesCount; index++) {
NSMutableArray *namesForSection = [outerArray objectAtIndex:index];
NSArray *sortedNamesForSection = [collation sortedArrayFromArray:namesForSection collationStringSelector:#selector(lowercaseString)];
[self.outerArray replaceObjectAtIndex:index withObject:sortedNamesForSection];
}
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [self.collation.sectionTitles count];
}
-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
NSString *theLetter = [self.collation.sectionTitles objectAtIndex:section];
if (![theLetter isEqualToString:#"#"]) {
NSString *titleString = [NSString stringWithFormat:#"%#", theLetter];
return titleString;
}
return nil;
}
-(NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
return self.collation.sectionTitles;
}
-(NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
return [self.collation sectionForSectionIndexTitleAtIndex:index];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSArray *innerArray = [self.outerArray objectAtIndex:section];
return [innerArray count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"cellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
// Get the inner array for this section
NSArray *innerArray = [self.outerArray objectAtIndex:indexPath.section];
// Get the name from the inner array
NSString *theName = [innerArray objectAtIndex:indexPath.row];
cell.textLabel.text = theName;
return cell;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainiPhoneStoryboard" bundle:nil];
DetailViewController *detailView = (DetailViewController *)[storyboard instantiateViewControllerWithIdentifier:#"DetailViewController"];
// Get the inner array for this section
NSArray *innerArray = [self.outerArray objectAtIndex:indexPath.section];
// Get the name from the inner array
NSString *tmpname = [innerArray objectAtIndex:indexPath.row];
detailView.lblname = tmpname;
[self presentViewController:detailView animated:YES completion:nil];
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.namesDictionary = [NSMutableArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"contacts" ofType:#"plist"]];
self.tableData = [namesDictionary valueForKey:#"name"];
self.collation = [UILocalizedIndexedCollation currentCollation];
[self configureSectionData];
}
Since you're populating your table from an array of just the names from your plist, you'll have to search that array using the name to find the dictionary that it belongs to, so you can pass that to the detail view controller (you would need to create a property in your detail view controller, passedInDictionary in my example):
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainiPhoneStoryboard" bundle:nil];
DetailViewController *detailView = (DetailViewController *)[storyboard instantiateViewControllerWithIdentifier:#"DetailViewController"];
// Get the inner array for this section
NSArray *innerArray = [self.outerArray objectAtIndex:indexPath.section];
// Get the name from the inner array
NSString *tmpname = [innerArray objectAtIndex:indexPath.row];
NSInteger indx = [self.namesDictionary indexOfObjectPassingTest:^BOOL(NSDictionary *dict, NSUInteger idx, BOOL *stop) {
return [dict[#"name"] isEqualToString:tmpname];
}];
NSDictionary *dict = self.namesDictionary[indx];
detailView.passedInDictionary = dict;
[self presentViewController:detailView animated:YES completion:nil];
}

Resources