update UITableView based on array - objective-c

I have JSON file who's information I put in my table. I have one button for sorting my array
I can sort my array and print it with NSLog. How can I update my table based on my sorted array?
This is my code:
-(void)sortArry:(UIButton *)sender
{
NSSortDescriptor *ageDescriptor = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES];
NSArray *sortDescriptors = #[ageDescriptor];
NSArray *sortedArray = [tableData sortedArrayUsingDescriptors:sortDescriptors];
//here i have my data in nslog
NSLog(#"%# sort test",sortedArray);
}
How can I show my sortedArray in the table?
I also used
[self.tableView reloadData];
after sorting but it didn't show the sorted table
Update:
Here is my cellForRowAtIndexPath
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath
*)indexPath
{
static NSString *CellIdentifier = #"Cell";
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *objects = [[NSBundle mainBundle] loadNibNamed:#"CustomCell" owner:nil options:nil];
for (id currentObject in objects)
{
if([currentObject isKindOfClass:[UITableViewCell class]])
{
cell = (CustomCell *) currentObject;
break;
}
}
}
id keyValuePair;
if (tableView == self.searchDisplayController.searchResultsTableView)
{
keyValuePair = [self.filteredTableData objectAtIndex:indexPath.row];
}
else
{
keyValuePair = [self.tableData objectAtIndex:indexPath.row];
}
cell.name.text = keyValuePair[#"name"];
return cell;
}

sortedArray is deleted when it goes out of scope at the end of your sortArry: method. You need to set the property or instance variable that your UITableViewDelegate methods inspect.

Call [tableView reloadData]; after the array is sorted.

sortedArray needs to be a property of your viewController not an instance variable of sortArry , so when you reload all UITableViewdelegate method can access its values and update.
// The array You use to populate the table
#property NSArray ArrayDataSource;
Change the method signature to return the sorted array
-(NSArray *)sortArry
{
NSSortDescriptor *ageDescriptor =
[[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES];
NSArray *sortDescriptors = #[ageDescriptor];
NSArray *sortedArray =
[tableData sortedArrayUsingDescriptors:sortDescriptors];
//here i have my data in nslog
NSLog(#"%# sort test",sortedArray);
return sortedArray;
}
Stored the return value in the datasource:
ArrayDataSource = [self.ArrayDataSource];
Finally reload the table
[self.tableView reloadData];

Related

Populating table with 2 arrays

I'm trying to populate a tableview controller with objects from 2 different arrays but it crashes and gives this error
"Terminating app due to uncaught exception 'NSRangeException', reason: ' -[__NSArrayM objectAtIndex:]: index 1 beyond bounds [0 .. 0]"***
how do i fix this? below is my code:
(void)viewDidAppear:(BOOL)animated{
//[super viewDidAppear:<#animated#>];
NSManagedObjectContext *bookmanagedObjectContext = [self managedObjectContext];
NSFetchRequest *bookfetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Book"];
NSPredicate *bookpredicate =[NSPredicate predicateWithFormat:#" bookref.toproject contains[cd]%#",self.projectdb];
[bookfetchRequest setPredicate:bookpredicate];
NSSortDescriptor *booksortDescriptor = [[NSSortDescriptor alloc]initWithKey:#"authorSurname" ascending:YES];
NSArray *booksortDescriptors = [[NSArray alloc]initWithObjects:booksortDescriptor, nil];
[bookfetchRequest setSortDescriptors:booksortDescriptors];
self.BookrefArray = [[bookmanagedObjectContext executeFetchRequest:bookfetchRequest error:nil] mutableCopy];
NSManagedObjectContext *journalmanagedObjectContext = [self managedObjectContext];
NSFetchRequest *journalfetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Journal"];
NSPredicate *journalpredicate =[NSPredicate predicateWithFormat:#" journalref.toproj contains[cd]%#",self.projectdb];
[journalfetchRequest setPredicate:journalpredicate];
NSSortDescriptor *journalsortDescriptor = [[NSSortDescriptor alloc]initWithKey:#"surname" ascending:YES];
NSArray *journalsortDescriptors = [[NSArray alloc]initWithObjects:journalsortDescriptor, nil];
[journalfetchRequest setSortDescriptors:journalsortDescriptors];
self.JournalrefArray = [[journalmanagedObjectContext executeFetchRequest:journalfetchRequest error:nil] mutableCopy];
[self.tableView reloadData];}
(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
#warning Potentially incomplete method implementation.
// Return the number of sections.
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
#warning Incomplete method implementation.
// Return the number of rows in the section.
return (self.BookrefArray.count + self.journalrefArray.count);
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cells";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
Journal *myjournal =[self.journalrefArray objectAtIndex:indexPath.row];
[cell.detailTextLabel setText:[myjournal valueForKey:#"journalname"]];
[cell.textLabel setText:[NSString stringWithFormat:#"%#, %#",[myjournal valueForKey:#"surname"],[myjournal valueForKey:#"firstname"]]];
Book *mybook =[self.BookrefArray objectAtIndex:indexPath.row];
// Configure the cell...
[cell.detailTextLabel setText:[mybook valueForKey:#"bookTitle"]];
[cell.textLabel setText:[NSString stringWithFormat:#"%#, %#",[mybook valueForKey:#"authorSurname"],[mybook valueForKey:#"authorOthernames"]]];
return cell;
}
You have this error because you try to access of an element of your array that is not exist.
In fact you says that your table has "table1.count + table2.count" elements and for each cell you try to get an element in your 2 tables with the current row.
For example, if table1 has 2 items and table2 has 5 items
, your tableView will have 7 rows and "cellForRowAtIndexPath" will be called 7 times. So for index 3 you will obtains this error because your table1 has only 2 items.
To solve the problem, you should get a book OR a journal in function of this row.
For example, you can try anything like this :
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cells";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (indexPath.row < self.BookrefArray.count)
{
Book *mybook =[self.BookrefArray objectAtIndex:indexPath.row];
// Configure your cell with a book
}
else
{
Journal *myjournal =[self.journalrefArray objectAtIndex:indexPath.row - self.BookrefArray.count];
// Configure your cell with a journal
}
return cell;
}
You can also change the order to display journals before books.
Answer :
You need to differentiate Book and Journal in -cellForRowAtIndexPath.
Explanation :
Your call for tableview's -cellForRowAtIndexPath will be dependent upon the number you're returning from tableview's -numberOfRowsInSection method.
So, for example, you've 3 objects in BookrefArray and 2 objects in journalrefArray, your tableview's -numberOfRowsInSection method will return 5 which means -cellForRowAtIndexPath will be called for 5 times.
Let's go through this line :
Journal *myjournal =[self.journalrefArray objectAtIndex:indexPath.row];
Here, it'll get indexPath.row = 5 for one case, and your journalrefArray contains only 2 objects. So, you're getting "index .. beyond bounds" error.
Update :
You can simply merge both the arrays into one.
[documentsArray addObjectsFromArray:self.BookrefArray];
[documentsArray addObjectsFromArray:self.journalrefArray];
and then in -cellForRowAtIndexPath, you can do something like this :
id document = [documentsArray objectAtIndex:indexPath.row];
if ([document isKindOfClass:Book])
{
Book *mybook = (Book *)document;
// Do something with mybook.
}
else
{
Journal *myjournal = (Journal *)document;
// Do something with myjournal.
}

Multiple sections in UITableView

Actually i'm using only one section. I sort my data stored in core data by date.
I want to have two sections (latest and history). In my first section "latest" I want to put my latest date and in the other section "history" i want to put other dates sorted by date.
My table is editable and I'm using NSFetchedResultsController.
Here is my sample code for numberOfRowsInSection:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc]init];
[fetchRequest setEntity:[NSEntityDescription entityForName:#"Info"
inManagedObjectContext:self.managedObjectContext]];
// Define how we want our entities to be sorted
NSSortDescriptor* sortDescriptor = [[[NSSortDescriptor alloc]
initWithKey:#"date" ascending:NO] autorelease];
NSArray* sortDescriptors = [[[NSArray alloc] initWithObjects:sortDescriptor, nil] autorelease];
[fetchRequest setSortDescriptors:sortDescriptors];
NSString *lower = [mxData.name lowercaseString];
NSPredicate *predicate = [NSPredicate predicateWithFormat: #"(name = %#)", lower];
[fetchRequest setPredicate:predicate];
NSError *errorTotal = nil;
NSArray *results = [self.managedObjectContext executeFetchRequest:fetchRequest error:&errorTotal];
if (errorTotal) {
NSLog(#"fetch board error. error:%#", errorTotal);
}
return [results count];
[fetchRequest release];
[results release];
}
You need to modify your designated "UITableViewDataSource" object to return "2" for the "numberOfSectionsInTableView:" method.
Then you need to return the right thing in your "tableView:cellForRowAtIndexPath:" method, depending on the section designated in the index path.
If you want an optional section title (e.g. "History" or "Latest"), you can also return an array of section titles via sectionIndexTitlesForTableView:.
Implement - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 2;
}
This way the tableviewController know's how many sections to create. If you don't implement this method it will create the default number of sections which is 1.
This method is asked to the data source to return the number of sections in the table view.
The default value is 1.
The full method description can be found here
Update:
When the tableview is asking you which cell to display for a certain index path you can give the cell with the right data. Presuming you have 2 NSArray's containing the titles for the latest and history rows you could do the following:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//create cell
static NSString *CellIdentifier = #"MyCellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(indexPath.section == 0){
//set title for latest
NSString *title = [[self latestTitles] objectAtIndex:indexPath.row];
[[cell textLabel] setText:title];
}else{
//set title for history
NSString *title = [[self historyTitles] objectAtIndex:indexPath.row];
[[cell textLabel] setText:title];
}
//Update: add NSLog here to check if the cell is not nil..
NSLog(#"cell = %#", cell);
return cell;
}

NSMutableArray works in ViewDidLoad, but not in DidSelectRowAtIndexPath

Menu.h
#interface Menu : UITableViewController {
NSMutableArray *arrayCellCollectionOrder;
NSMutableDictionary *dictCellCollection;
NSMutableDictionary *dictCellIndividual;
}
#property (nonatomic, retain) NSMutableArray *arrayCellCollectionOrder;
#end
Menu.m
ViewDidLoad works as normal.
#synthesize arrayCellCollectionOrder;
- (void)viewDidLoad {
// Codes to read in data from PLIST
// This part works
NSString *errorDesc = nil;
NSPropertyListFormat format;
NSString *plistPath;
NSString *rootPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
plistPath = [rootPath stringByAppendingPathComponent:#"InfoTableDict.plist"];
if (![[NSFileManager defaultManager] fileExistsAtPath:plistPath]) {
plistPath = [[NSBundle mainBundle] pathForResource:#"InfoTableDict" ofType:#"plist"];
}
NSData *plistXML = [[NSFileManager defaultManager] contentsAtPath:plistPath];
NSDictionary *temp = (NSDictionary *)[NSPropertyListSerialization
propertyListFromData:plistXML
mutabilityOption:NSPropertyListMutableContainersAndLeaves
format:&format
errorDescription:&errorDesc];
if (!temp) {
NSLog(#"Error reading plist: %#, format: %d", errorDesc, format);
}
arrayCellCollectionOrder = [[[NSMutableArray alloc] init] retain];
arrayCellCollectionOrder = [temp objectForKey:#"CellCollectionOrder"];
// I can access `arrayCellCollectionOrder` here, it's working.
}
cellForRowAtIndexPath works as normal. I can access arrayCellCollectionOrder.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"PhotoCell";
PhotoCell *cell = (PhotoCell *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"PhotoCell" owner:self options:nil];
for (id currentObject in topLevelObjects) {
if ([currentObject isKindOfClass:[PhotoCell class]]) {
cell = (PhotoCell *) currentObject;
break;
}
}
}
// Copy the specific dictionary from CellCollection to Cell Individual
dictCellIndividual = [dictCellCollection objectForKey:[NSString stringWithFormat:#"%#", [arrayCellCollectionOrder objectAtIndex:indexPath.row]]];
cell.photoCellTitle.text = [dictCellIndividual objectForKey:#"Title"]; // Load cell title
cell.photoCellImage.image = [UIImage imageNamed:[NSString stringWithFormat:#"%#", [dictCellIndividual objectForKey:#"ThumbnailFilename"]]]; // Load cell image name
return cell;
}
didSelectRowAtIndexPath NOT WORKING. I cannot access arrayCellCollectionOrder.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Browser
NSMutableArray *arrayPhotos = [[NSMutableArray alloc] init];
NSLog(#"indexPath.row = %d", indexPath.row); // Returns the row number i touched, works.
NSLog(#"arrayCellCollectionOrder = %#", [NSString stringWithFormat:#"%#", [arrayCellCollectionOrder objectAtIndex:indexPath.row]]); // DOES NOT WORK.
// Copy the specific dictionary from CellCollection to Cell Individual
dictCellIndividual = [dictCellCollection objectForKey:[NSString stringWithFormat:#"%#", [arrayCellCollectionOrder objectAtIndex:indexPath.row]]]; // This similar line gives error too.
... ...
... ...
... ...
... ...
}
Error is:
* Terminating app due to uncaught exception 'NSRangeException', reason: '-[__NSCFArray objectAtIndex:]: index (1) beyond bounds (0)'
i.e.: I clicked on row 1, but arrayCellCollectionOrder is NULL.
There should have data in arrayCellCollectionOrder as it's declared in ViewDidLoad.
Is there something that I missed out?
Thanks a lot in advance.
arrayCellCollectionOrder = [[[NSMutableArray alloc] init] retain];
arrayCellCollectionOrder = [temp objectForKey:#"CellCollectionOrder"];
Do you see what you are doing to arrayCellCollectionOrder? You first assign it to a new NSMutableArray (and retain it needlessly), and then you immediately orphan the array and assign arrayCellCollectionOrder to another object that you are getting from the temp dictionary. In other words, that first line isn't doing anything for you, other than create a leaked mutable array.
If the second line is correct and you are getting a valid object and that is what you want, then the problem is that I don't see where that object is getting retained. As long as it is in the dictionary, it is probably retained, but if temp is discarded, then its members are released. If you did a
self.arrayCellCollectionOrder = [temp objectForKey:#"CellCollectionOrder"];
then the setter would retain it.

How to Fix EXC_BAD_ACCESS on NSArray Property?

This is yet another EXC_BAD_ACCESS question. Although I've done my homework and am certain that I am not over-releasing my NSArray.
So here is a snippet of my code:
tableData = [NSDictionary dictionaryWithJSONString:JSONstring error:&error];
//Collect Information from JSON String into Dictionary. Value returns a mutli
dimensional NSDictionary. Eg: { value => { value => "null"}, etc }
NSMutableArray *t_info = [[NSMutableArray alloc] init];
for(id theKey in tableData)
{
NSDictionary *get = [tableData objectForKey:theKey];
[t_info addObject:get];
[get release];
} // converting into an NSArray for use in a UITableView
NSLog(#"%#", t_info);
//This returns an Array with the NSDictionary's as an Object in each row. Returns fine
if (tvc == nil)
{
tvc = [[tableViewController alloc] init]; //Create Table Controller
tableView.delegate = tvc;
tableView.dataSource = tvc;
tvc.tableView = self.tableView;
tvc.tableData = t_info; //pass our data to the tvc class
[tvc.tableView reloadData];
}
...
Now in my TableViewController Class:
#implementation tableViewController
#synthesize tableData, tableView;
- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [tableData count]; //Returns X Amount Fine.
}
- (UITableViewCell *)tableView:(UITableView *)the_tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
NSString *MyIdentifier = [NSString stringWithFormat:#"MyIdentifier"];
UITableViewCell *cell = [the_tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:MyIdentifier] autorelease];
}
NSLog(#"%#", tableData); //** CRASHES!!**
cell.textLabel.text = #"This is a test";
return cell;
}
If I were to comment out that NSLog, it'll work fine and return "this is a test" on each table row.
This one has really got me stumped, all the articles I have around about this problem is generally related to retain/memory issues.
Also, another important point.
If I were to pass through my original (NSDictionary) tableData from my first class code and run the same script in my tableViewController - I can NSLog the object perfectly fine.
The only time you need to release an object is if you have explicitly allocated it by way of new, alloc, or copy.
NSMutableArray *t_info = [[NSMutableArray alloc] init];
for(id theKey in tableData)
{
NSDictionary *get = [tableData objectForKey:theKey];
[t_info addObject:get];
[get release];
}
You shouldn't be releasing get here. By doing this, you're releasing the reference that the tableData dictionary is holding onto, which is bad. My guess is that this is what is causing the problem that you're encountering.
If I'm not mistaken, the reason why [tableData count] returns the expected value is because the array is still holding onto the references that have been released.

NSDictionary to TableView

because i'm a newby at Stackoverflow i cannot comment someones anwser yet. (my reputation is 16..). I got a question about this anwser: How do I put this JSON data into my table view? Please help me, I'm living in a nightmare :)
Fulvio sais you have to use [eventNameList addObject:event]; and [eventNameList objectAtIndex:indexPath.row]; to store and get the event data but. addObject is an NSMutableSet method and objectAtIndex:indexPath.row is not. So i cannot use this method to get the data from the NSMutableSet.
Besides that, i can use the count methods neither.
Any Idea's ?
Assuming you have an NSDictionary, you could use the [dictionary allKeys] method to retrieve an array with all keys (lets call it keyArray for now). For the rowCount you could return the count of objects in this keyArray. To get the item that needs to be displayed in the cell you could use [dictionary objectForKey:[keyArray objectAtIndex:indexPath.row]]] to get the appropriate dictionary for the displayed cell.
In code:
// use the keyArray as a datasource ...
NSArray *keyArray = [jsonDictionary allKeys];
// ------------------------- //
// somewhere else in your code ...
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [keyArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
// set some cell defaults here (mainly design) ...
}
NSString *key = [keyArray objectAtIndex:indexPath.row];
NSDictionary *dictionary = [jsonDictionary objectForKey:key];
// get values from the dictionary and set the values for the displayed cell ...
return cell;
}
#Tieme: apparantly the URL you use already returns an array, you don't really need to process a dictionary (you could just use the array as the dataSource), check out the following:
SBJSON *json = [[[SBJSON alloc] init] autorelease];
NSURL *url = [NSURL URLWithString:#"http://www.my-bjoeks.nl/competitions/fetchRoutes/25.json"];
NSString *string = [[[NSString alloc] initWithContentsOfURL:url] autorelease];
NSError *jsonError = nil;
id object = [json objectWithString:string error:&jsonError];
if (!jsonError) {
NSLog(#"%#", object);
NSLog(#"%#", [object class]); // seems an array is returned, NOT a dictionary ...
}
// if you need a mutableArray for the tableView, you can convert it.
NSMutableArray *dataArray = [NSMutableArray arrayWithArray:object]
eventNameList should be defined as an NSMutableArray, not an NSMutableSet. NSMutableArray responds to both -addObject (it puts the new object at the end of the array) and -objectAtIndex: and when you think about it, a table view is essentially an ordered list and so is an array whereas a set is not.
LUCKY:)
Assuming that you might be having nsmutablearray of nsdictionary.
In such case you can get data using:
[dictionary objectforkey:#"key"] objectAtIndex:indexpath.row]