UITableView and alphabetical order - iphone

In my app I parse an XML file then I would like to show the entry of this file in a UITableView. I found on the web how to make section in alphabetical order (like iPhone contacts), it works fine with my app. When i tap on a row in my tableview I want to display another ViewController, in which I will find some information about the row I tapped, but I'm having some problem: when I tap on a row the variable indexPath.row refers to the section and the information in new view controller aren't right. I will post here some screenshot to show you want I'm trying to explain.
In the following pictures you can see how the app should work:
In the following pictures you can see the error of my app:
You can see that in picture 1 the name is the same and in picture 2 you can see that the name it's wrong. I guess it depend on the variable indexPath.row. I will post here the code to create and populate the tableview:
#import "TableWasteViewController.h"
#import "WasteXmlParser.h"
#import "WasteDetailViewController.h"
#interface TableWasteViewController ()
#property(nonatomic,strong)NSArray *arrayWastes;
#property(nonatomic,strong)NSMutableArray *typeOfWaste;
#property(nonatomic,strong)NSMutableArray *typeOfBin;
#property(nonatomic,strong)NSMutableArray *indexWastes;
#property(nonatomic,strong)NSMutableArray *typeOfWasteBackup;
#end
#implementation TableWasteViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
WasteXmlParser *parser = [[WasteXmlParser alloc]init];
[parser parseWasteXml];
self.arrayWastes = [[NSArray alloc]init];
self.arrayWastes = [parser.arrayWastes mutableCopy];
self.indexWastes = [[NSMutableArray alloc]init];
self.typeOfWaste = [[NSMutableArray alloc]init];
self.typeOfBin = [[NSMutableArray alloc]init];
for (int i = 0; i < [self.arrayWastes count]; i++) {
[self.typeOfWaste addObject:[[self.arrayWastes objectAtIndex:i] objectForKey:#"type"]];
[self.typeOfBin addObject:[[self.arrayWastes objectAtIndex:i]objectForKey:#"place"]];
}
for (int i = 0; i < [self.typeOfWaste count]-1; i++) {
char alphabet = [[self.typeOfWaste objectAtIndex:i] characterAtIndex:0];
NSString *uniChar = [NSString stringWithFormat:#"%c", alphabet];
if (![self.indexWastes containsObject:uniChar]) {
[self.indexWastes addObject:uniChar];
}
}
self.typeOfWasteBackup = [self.typeOfWaste mutableCopy];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [self.indexWastes count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return [self.indexWastes objectAtIndex:section];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSString *alphabet = [self.indexWastes objectAtIndex:section];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF beginswith[c] %#",alphabet];
NSArray *wastes = [self.typeOfWaste filteredArrayUsingPredicate:predicate];
return [wastes count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
UIFont *myFont = [UIFont fontWithName:#"Arial" size:14.0];
if (cell == nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
NSString *alphabet = [self.indexWastes objectAtIndex:[indexPath section]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF beginswith[c] %#", alphabet];
NSArray *wastes = [self.typeOfWaste filteredArrayUsingPredicate:predicate];
if ([wastes count] > 0) {
NSString *cellValue = [wastes objectAtIndex:indexPath.row];
cell.textLabel.font = myFont;
cell.textLabel.numberOfLines = 2;
cell.textLabel.text = cellValue;
}
return cell;
}
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
return self.indexWastes;
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
NSIndexPath *indexPath = [self.tableWaste indexPathForSelectedRow];
WasteDetailViewController *vc = segue.destinationViewController;
vc.typeOfWaste = [self.typeOfWaste objectAtIndex:indexPath.row];
vc.typeOfBin = [self.typeOfBin objectAtIndex:indexPath.row];
vc.urlPic = [self.arrayWastes[indexPath.row]objectForKey:#"imgUrl"];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
- (IBAction)backToHome:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
#end
I hope you can help me to fix this issue. Thank you

Rows are indexed by section, i.e. the first item in each section has indexPath.row == 0. So in order to look up values in your flattened vc.typeOfWaste and vc.typeOfBin arrays, you're going to need to do something along the lines of your numberOfRowsInSection method, where you filter the flattened array by the alphabet character and then get the item of the filtered array using indexPath.row.
Overall, this approach seems rather messy, having to filter your data repeatedly. Your data structures don't map well to the problem being solved. I would recommend using the TLIndexPathTools data model TLIndexPathDataModel because it's specifically designed for tables and collection views, can organize your data into sections, and can look up items by index path. Would be happy to walk you through a refactor if you like.

It's because the arrays that you're using to pass the data to the WasteDetailViewController, which are "typeOfWaste, typeOfBin, and urlPic" aren't sorted. The sorted array is called "wastes", but it is only available within the numberOfRowsInSection and the cellForRowAtIndexPath methods. You need to be passing the data in the wastes array forward, so rather than sorting the wastes array all the time, just sort it once after you've loaded it.
Add this property:
#interface TableWasteViewController ()
#property (strong, nonatomic) NSArray *sortedWastes;
#end
Now in viewDidLoad
NSString *alphabet = [self.indexWastes objectAtIndex:section];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF beginswith[c] %#",alphabet];
self.sortedWastes = [self.typeOfWaste filteredArrayUsingPredicate:predicate];
Finally, in prepareForSegue
vc.typeOfWaste = [self.sortedWastes objectAtIndex:indexPath.row];
Your problems all stem from the fact that you are displaying a sorted array, but the array that you are using to pass the data forward is the unsorted array, so the indexPath is completely useless.
Furthermore, your typeOfBin and urlPic are going to be wrong as well. You need to find some way to link all three of your arrays together, so that when you sort one, you sort them all. The method above only keeps your typeOfWaste array sorted.

I also faced this issue few days ago...u need to make one class set properties whatever you want and then add that object in one array after that u can sort array on one property then whole array will be sorted
#import <Foundation/Foundation.h>
#interface HomeFeed : NSObject
#property (nonatomic, copy) UIImage *ItemImage;
#property (nonatomic, copy) NSString *ItemTitle;
#property (nonatomic, copy) NSString *ItemDate;
#property (nonatomic, copy) NSString *ItemDescription;
#property (nonatomic, copy) NSString *ItemHours;
#property (nonatomic, copy) NSString *ItemID;
#property (nonatomic, copy) NSString *itemDetailUrl;
#property (nonatomic, copy) NSString *itemPerson;
#property (nonatomic, copy) NSString *itemThumbUrl;
#property (nonatomic, assign) int ItemDuration;
#end
#import "HomeFeed.h"
#implementation HomeFeed
#synthesize ItemTitle=_ItemTitle, ItemDate=_ItemDate, ItemImage=_ItemImage,ItemID=_ItemID,ItemDuration=_ItemDuration,ItemDescription,ItemHours=_ItemHours,itemDetailUrl,itemPerson,itemThumbUrl;
#end
NSArray*arr=[responseString JSONValue];
NSLog(#"Json Dictionary speakersss : %#",arr);
NSLog(#"Json arr count speaker : %i",arr.count);
for (int i=0; i<arr.count; i++) {
NSDictionary *dict=[[ NSDictionary alloc]init];
dict=[arr objectAtIndex:i];
HomeFeed *feed = [[HomeFeed alloc] init];
feed.ItemTitle = [NSString stringWithFormat:#"%#%#%#",[dict objectForKey:#"firstName"],#" ",[dict objectForKey:#"lastName"]];
feed.ItemDuration = [[NSString stringWithFormat:#"%#", [dict objectForKey:#"count"]] intValue];
feed.itemDetailUrl=[dict objectForKey:#"detailsUrl"];
[self.itemsDataArray addObject:feed];
[itemTitle addObject:feed.ItemTitle];
}
HomeFeed *feed = [[HomeFeed alloc] init];
NSSortDescriptor *sorter = [[NSSortDescriptor alloc] initWithKey:#"ItemTitle" ascending:YES];
[self.itemsDataArray sortUsingDescriptors:[NSArray arrayWithObject:sorter]];
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"SpeakersCustomCell"];
cell.selectionStyle = NO;
cell.accessoryType = NO;
HomeFeed *feed = [self.itemsDataArray objectAtIndex:indexPath.row];
UIImageView *arrow = (UIImageView *)[cell viewWithTag:3];
arrow.image = [UIImage imageNamed:#"accessory.png"];
UILabel *lblLeft = (UILabel *)[cell viewWithTag:1];
UILabel *lblRight = (UILabel *)[cell viewWithTag:2];
lblLeft.text=feed.ItemTitle;
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
HomeFeed *feed=(HomeFeed*)[self.itemsDataArray objectAtIndex:indexPath.row];
self.onlinDetail.strUrl=feed.itemDetailUrl;
NSString *key = #"OrientationStringValue";
NSDictionary *dictionary = [NSDictionary dictionaryWithObject:feed.itemDetailUrl forKey:key];
[[NSNotificationCenter defaultCenter] postNotificationName:#"NotifSpeaker1" object:nil userInfo:dictionary];
}

Related

NSCoding help very strange error access to deallocated instance

In my code I store in the memory my array... To obtain it without make an HTML Request.
the first time, when I populated my array everything is ok... the problems show up when I load array from the memory and load them in a table.
That's the class
#interface SubscriptionArray : NSObject{
NSString *title;
NSString *source;
NSString *htmlUrl;
NSInteger count;
}
#property (nonatomic,retain) NSString *title;
#property (nonatomic,retain) NSString *source;
#property (nonatomic,retain) NSString *htmlUrl;
#property (nonatomic) NSInteger count;
#end
#import "SubscriptionArray.h"
#implementation SubscriptionArray
#synthesize title,source,htmlUrl,count;
-(void)dealloc{
[title release];
[source release];
[htmlUrl release];
}
#pragma mark NSCoding
- (id)initWithCoder:(NSCoder *)decoder {
if (self = [super init]) {
title = [[decoder decodeObjectForKey:#"title"] retain];
source = [[decoder decodeObjectForKey:#"source"] retain];
htmlUrl = [[decoder decodeObjectForKey:#"htmlUrl"] retain];
count = [decoder decodeIntegerForKey:#"count"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)encoder {
if (title) [encoder encodeObject:title forKey:#"title"];
if (source) [encoder encodeObject:source forKey:#"source"];
if (htmlUrl) [encoder encodeObject:htmlUrl forKey:#"htmlUrl"];
if (count) [encoder encodeInteger:count forKey:#"count"];
}
The array is declared in the delegate (to share with 3 different classes) as
NSMutableArray *onlySubsriptions; #property(nonatomic, retain)
NSMutableArray *onlySubsriptions;
And I load it in this way
[onlySubsriptions removeAllObjects];
self.onlySubsriptions = [NSKeyedUnarchiver unarchiveObjectWithFile:fullFileName];
And I received an error in the cellForRowAtIndexPath
for example I'm sure that I have 2 elements, the cellForRowAtIndexPath throws an exception when it start to load the 1st element (element 0)... it says deallocated instance.
I'm sure that the problem is on the encoding because the 1st time when I get the array everything is fine... Problems show up only when the next time I load my array that I store locally.
**** MORE INFO ABOUT ******
**** cellForRowAtIndexPath
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
TDBadgedCell *cell = [[[TDBadgedCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
SubscriptionArray * element=[[SubscriptionArray alloc]init];
if (indexPath.section==0) {
NSLog(#"CASO SPECIALELEMENTS");
element =[delegate.reader.specialElements objectAtIndex:[indexPath row]];
}
if (indexPath.section==1) {
NSLog(#"CASO CATEGORIA");
element =[delegate.reader.categories objectAtIndex:[indexPath row]];
}
if (indexPath.section==2) {
NSLog(#"CASO SUBSCRIPTIONS");
element =[delegate.reader.onlySubsriptions objectAtIndex:[indexPath row]];
}
To deal with this kind of exception, you can use malloc instrument to find out the zombie and fix it.
Product > Profile will launch Instruments and then you there should be a "Trace Template" named "Malloc". This question may be useful for you.

UITableView does not show new entries when data object conforms to NSCoding

When I make an object class conform to NSCoding, the tableView does not show new entries. Suspected it had to do with the object initialization but cannot solve it. Might be very basic but cannot find the error. Here is the code I simplified to post:
Custom Object:
// DataObject.h
#import <Foundation/Foundation.h>
#interface DataObject : NSObject {
NSString *name;
}
#property (nonatomic, copy) NSString *name;
#end
// DataObject.m
#import "DataObject.h"
#implementation DataObject
#synthesize name;
- (void)encodeWithCoder:(NSCoder*)encoder {
[encoder encodeObject:name forKey:#"name"];
}
- (id)initWithCoder:(NSCoder *)decoder {
self = [super init];
if (!self) return nil;
name = [[decoder decodeObjectForKey:#"name"] retain];
return self;
}
These is the TableView root controller - Have omitted a few methods:
// RootViewController.h
#import <UIKit/UIKit.h>
#interface RootViewController : UITableViewController {
NSMutableArray *list;
}
#property (nonatomic, retain) NSMutableArray *list;
- (void)add:(id)sender;
- (NSString *)dataFilePath;
#end
// RootViewController.m
#import "RootViewController.h"
#import "DataObject.h"
#implementation RootViewController
#synthesize list;
- (void)add:(id)sender
{
DataObject *newEntry = [[DataObject alloc] init];
newEntry.name = #"Willy";
[self.list addObject:newEntry];
[self.tableView reloadData];
// Scroll table view to last row
if ([list count] > 1) {
NSUInteger index = list.count - 1;
NSIndexPath *lastRow = [NSIndexPath indexPathForRow:index inSection: 0];
[self.tableView scrollToRowAtIndexPath: lastRow atScrollPosition: UITableViewScrollPositionTop animated: YES];
}
[newEntry release];
}
- (NSString *)dataFilePath
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:#"datafile"];
}
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.rightBarButtonItem = self.editButtonItem;
self.title = #"Names";
self.list = [[NSMutableArray alloc] init];
NSMutableArray *tempArray = [NSKeyedUnarchiver unarchiveObjectWithFile:[self dataFilePath]];
self.list = tempArray;
UIApplication *app = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationWillResignActive:)
name:UIApplicationWillResignActiveNotification
object:app];
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
self.navigationItem.rightBarButtonItem = self.editButtonItem;
UIBarButtonItem *addButton = [[UIBarButtonItem alloc]
initWithTitle:#"Add"
style:UIBarButtonItemStylePlain
target:self
action:#selector(add:)];
self.navigationItem.leftBarButtonItem = addButton;
[addButton release];
[self.tableView reloadData];
}
- (void)applicationWillResignActive:(NSNotification *)notification;
{
[NSKeyedArchiver archiveRootObject:self.list toFile:[self dataFilePath]];
}
- (void)viewWillAppear:(BOOL)animated {
[self.tableView reloadData];
[super viewWillAppear:animated];
}
// Customize the number of sections in the table view.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [list 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];
}
// Configure the cell.
NSUInteger row = [indexPath row];
DataObject *oneName = [self.list objectAtIndex:row];
cell.textLabel.text = oneName.name;
return cell;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source.
NSUInteger row = [indexPath row];
[self.list removeObjectAtIndex:row];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}
#end
Oh, man I am so embarrassed...It had nothing to do with NSCoding directly but when I made the data class NSCoding compliant I added in viewDidLoad:
NSMutableArray *tempArray = [NSKeyedUnarchiver unarchiveObjectWithFile:[self dataFilePath]];
self.list = tempArray;
The first line is fine, but the second assigned a new pointer address to self.list and the tempArray was later autoreleased...
As this line was not executed in the "not NSCoding compliant" version it appeared to work then and not when encodeWithCoder and initWithCoder were implemented.
Because of this self.list was nil instead of being initialized and ready when in the add: method I added a new instance. It was created fine, but never added to the array and therefore never showed in the tableView.
This was easily fixed using instead in viewDidLoad:
NSMutableArray *tempArray = [NSKeyedUnarchiver unarchiveObjectWithFile:[self dataFilePath]];
[self.list addObjectsFromArray:tempArray];
Worst part is that I was pulling my hair because of this and was convinced it was because of NSCoding and the initialization of objects...

UITableView abcd list indexing

I need to implement the abcd list indexing in the below table view right side.when i am clicking on a/b/c etc table view need to show the entries according to that for sorting like iphone contacts .may i know what are all
the changes i need for it.is need to create sections for that?please any one explain me
any solution.
I have an class named as "myClass" which contains the properties iD, name and imageURL.
image url holds the photolibrary alasset url.
myClass.h
#interface myClass: NSObject {
NSInteger iD;
NSString *name;
NSString *imageURL;
}
#property (nonatomic, assign) NSInteger iD;
#property (nonatomic, assign) NSString *name;
#property (nonatomic, assign) NSString *imageURL;
myClass.m
#implementation myClass
#synthesize iD;
#synthesize name;
#synthesize imageURL;
#end
So I added 50 image details with iD, name, imageURL as myClass objects in to an NSMutableArray named as *BundleImagesArray *
i displayed it in a table view. my code is:
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger) section {
//getting all image details with iD,name,imageURL as **myClass** objects
BundleImagesArray = [staticDynamicHandler getImageFromBundle];
int count = [BundleImagesArray count];
for (int i = 0; i<count; i++) {
//this array for easy scrolling after first time the table is loaded.
[imageCollectionArrays addObject:[NSNull null]];
}
return count;
}
cellForRowAtIndexPath
- (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];
}
//removing all the subviews
if ([cell.contentView subviews]) {
for (UIView *subview in [cell.contentView subviews]) {
[subview removeFromSuperview];
}
}
cell.selectionStyle = UITableViewCellSelectionStyleNone;
[cell setAccessoryType:UITableViewCellAccessoryNone];
myClass *temp = [BundleImagesArray objectAtIndex:indexPath.row];
//adding image view
UIImageView *importMediaSaveImage=[[[UIImageView alloc] init] autorelease];
importMediaSaveImage.frame=CGRectMake(0, 0, 200,135 );
[cell.contentView addSubview:importMediaSaveImage];
//adding image label
UILabel *sceneLabel=[[[UILabel alloc] initWithFrame:CGRectMake(220,0,200,135)] autorelease];
sceneLabel.font = [UIFont boldSystemFontOfSize:16.0];
sceneLabel.textColor=[UIColor blackColor];
[cell.contentView addSubview:sceneLabel];
sceneLabel.text = temp.name;
if([imageCollectionArrays objectAtIndex:indexPath.row] == [NSNull null]){
//getting photlibrary image thumbnail as NSDAta
NSData *myData = [self photolibImageThumbNailData::temp.imageURL]
importMediaSaveImage.image =[UIImage imageWithData:myData ];
[imageCollectionArrays replaceObjectAtIndex:indexPath.row withObject:importMediaSaveImage.image];
} else {
importMediaSaveImage.image = [imageCollectionArrays objectAtIndex:indexPath.row];
}
temp = nil;
return cell
}
*Note:*
i done it using the following way
1.Number of sections-
inside this we are getting the label array and then calling the method countArrayWords whihc will count the words and
the alphabets starting from a particular word.
2.Title for headerin section-
Used for gettting the headers for the sections.Currently we are not using this methd as it will not look good when we are having
no data on the table view.
3.number of rows in section-
in this we are calling the method countArrayWords if the sectio=0.
4.cell for row at index path-
this is the method in which we are calculating a variable known as h through which we are using to bind the images to the table
view.
However the Tableview performence is too slow with 1000 images
An easy way to handle this is to create 2 arrays, one of sections, and another of section titles. You can then populate these from your sorted array of MyClass'es in your init method (initWithObjects: , say)
- (id) initWithObjects: (NSArray *) myObjectsArray
{
self = [super initWithNibName: <your-nib-name> bundle: nil];
if (self) {
self.sections = [NSMutableArray array];
self.sectionTitles = [NSMutableArray array];
for (MyClass * object in self.myObjectsArray) {
NSMutableArray * section = [self.sections lastObject];
if (!section || ![[[[section lastObject] name] substringToIndex: 1] isEqualToString: [object.name substringToIndex: 1]]) {
// Create a new section on change of first character
[self.sections addObject: [NSMutableArray array]];
[self.sectionTitles addObject: [object.name substringToIndex: 1]];
}
[[self.sections lastObject] addObject: object];
}
}
return self;
}
Then your datasource methods are
- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView
{
return [self.sections count];
}
- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [[self.sections objectAtIndex: section] count];
}
- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//Create cell
MYClass * object = [[self.sections objectAtIndex: indexPath.section] objectAtIndex: indexPath.row];
//Configure cell
return cell;
}
- (NSArray *) sectionIndexTitlesForTableView:(UITableView *)tableView
{
return self.sectionTitles;
}

Sort UITableview in objective c with dates descending NSDictionary

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.

Displaying .plist key values alphabetically in an UITableView

I have an array of dictionaries in an iOS .plist structured similar to the following:
<plist version="1.0">
<array>
<dict>
<key>name</key>
<string>Afghanistan</string>
<key>government</key>
<string>Islamic Republic</string>
<key>population</key>
<integer>29121286
</integer>
</dict>
<dict>
<key>name</key>
<string>Albania</string>
<key>government</key>
<string>Emerging Democracy</string>
<key>population</key>
<integer>2986952</integer>
</dict>
I am trying to load the <key>name</key> from each dictionary into an NSTableViewCell then display them all alphabetically in an NSTableView similar to the Contacts App in iOS.
Below are my ViewControllers .h and .m. The sort is working, but I am not able to load the results into the TableViewCells?
FirstViewController.h
#import <UIKit/UIKit.h>
#interface FirstViewController : UIViewController <UITableViewDelegate,UITableViewDataSource>
{
NSArray *sortedCountries;
}
#property (nonatomic, retain) NSArray *sortedCountries;
#end
FirstViewController.m
#import "FirstViewController.h"
#implementation FirstViewController
#synthesize sortedCountries;
-(void)viewDidLoad {
NSString *path = [[NSBundle mainBundle] pathForResource:#"countries"ofType:#"plist"];
NSArray *countries = [NSArray arrayWithContentsOfFile:path];
NSSortDescriptor *descriptor = [[[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES] autorelease];
NSArray *sortedCountries = [[countries sortedArrayUsingDescriptors:[NSArray arrayWithObject:descriptor]] retain];
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section {
return 2;
}
-(UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSDictionary *country = [sortedCountries objectAtIndex:indexPath.row];
NSString *countryName = [country objectForKey:#"name"];
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell =
[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier] autorelease];
}
cell.textLabel.text = countryName;
return cell;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[sortedCountries release];
[super dealloc];
}
#end
EDIT: Another question related to this here.
Add an ivar to your view controller's #interface in the header file:
#interface MyViewController : UITableViewController
{
...
NSArray *sortedCountries;
}
Add this code (to read and sort the plist by country name) to your view controller's initWith... method:
NSArray *countries = [NSArray arrayWithContentsOfFile: pathToPlist];
// Now the array holds NSDictionaries, sort 'em:
NSSortDescriptor *descriptor = [[[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES] autorelease];
sortedCountries = [[countries sortedArrayUsingDescriptors:[NSArray arrayWithObject:descriptor]] retain];
Then use the following snippet to extract the values:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *country = [sortedCountries objectAtIndex:indexPath.row];
NSString *countryName = [country objectForKey:#"name"];
NSString *governmentType = [country objectForKey:#"government"];
NSSInteger population = [[country objectForKey:#"population"] integerValue];
// ... do something with countryName, governmentType, population
}
Don't forget to release sortedCountries:
- (void)dealloc
{
...
[sortedCountries release];
[super dealloc];
}
Create an NSArray for your file:
NSArray *iOSPlist = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"iOS" ofType:#"plist"]];
then in this method write after if (cell == nil){
}:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
cell.textLabel.text = [[iOSPlist objectAtIndex:indexPath.row] objectForKey:#"name"];
}
and don't forget to return [iOSPlist count] in the - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section method;
Here is an example pulling the version number out of the info.plist. Use something similar to pull out your name key ( objectForKey:#"name")
NSString *path = [[NSBundle mainBundle] bundlePath];
NSString *finalPath = [path stringByAppendingPathComponent:#"Info.plist"];
plist = [[NSDictionary dictionaryWithContentsOfFile:finalPath] retain];
NSString* version = [plist objectForKey:#"CFBundleVersion"];
Here's a StackOverflow question on working with data in plists. The answers get quite detailed.
Parse Plist (NSString) into NSDictionary