Populating table with 2 arrays - objective-c

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

Related

update UITableView based on array

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

how to instantiate a CD entity without being fetched

I have a program that fetches from Core Data and then within the program if something is fetches it displays the results in my UITableView.
Now my problem is that sometimes I need the user to add another UITableCell to my UITableView but my tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section counts the elements in my array which holds my ExerciseData entities.
I've tried to create an ExerciseData entity as if it was an object but this doesnt work and crashes the program.
by doing
if ([[_ed objectAtIndex:indexPath.row] weight]) {
NSLog(#"%#",_ed);
cell.weightInput.text = [NSString stringWithFormat:#"%#",[[_ed objectAtIndex:indexPath.row] weight]];
}
it throws the error CoreData: error: Failed to call designated initializer on NSManagedObject class 'ExerciseData' that made me understand that I cant initiate a CD entity like that.
then I've tried to add to the NSArray _ed a NSString and then check for the class and if the class type was a class of NSString it shouldnt try to set cell.weightInput.text
My question are 2:
1) Is there a way to initiate an entity so that I can insert it in the array and then check if is empty so that then i can validate it on a later if statement?
if that is not possible
2) How could i populate the NSArray with something that can then be validated & escalated to multimple items but also work when there is a ExerciseData?
My goal is that the person uses - addSet to create a new UITableCell for as many times the button is pressed then i need to validate whatever is inside so that if is a ExerciseData entity and the property weight is set to populate cell.weightInput.text or otherwise dont populate it
- (void) addNewSet {
ExerciseData * newEd = [[ExerciseData alloc] init];
NSMutableArray* ar = [[NSMutableArray alloc]initWithArray:_ed];
[ar addObject:newEd];
_ed = [[NSArray alloc] initWithArray:ar];
[_mytable reloadData];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
workoutCell * cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
if (!cell) {
cell = [[workoutCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"cell"];
}
cell.weightInput.text = #"";
if ( indexPath.row < [_ed count] ) {
if (![[[_ed objectAtIndex:indexPath.row] class] isKindOfClass:[NSString class]]) {
NSLog(#"%#",_ed);
cell.weightInput.text = [NSString stringWithFormat:#"%#",[[_ed objectAtIndex:indexPath.row] weight]];
}
}
//...
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if ( [_ed count] == 0 ) {
return 1;
} else {
return [_ed count];
}
}
To create a new CoreData entity you need to:
ExerciseData *data = (ExerciseData*)[NSEntityDescription insertNewObjectForEntityForName:#"ExerciseData" inManagedObjectContext:yourContext];
Make sure to save your context after!

NSFetchedResultsController -> How to implement sections?

I am having trouble getting sections with a NSFetchedResultsController working. I have an entity, say 'Employee' and a string attribute, let's say 'name'. Now I want to show all employee's names in a UITableView using a NSFetchedResultsController... no problem, heres my Code:
if (_fetchedResultsController == nil) {
NSManagedObjectContext *moc = [appDelegate managedObjectContext];
NSFetchRequest *request = [NSFetchRequest new];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Employee" inManagedObjectContext:moc];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"name" ascending:YES];
[request setEntity:entity];
[request setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];
_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:moc sectionNameKeyPath:#"name" cacheName:#"root"];
NSError *error;
[_fetchedResultsController performFetch:&error];
if (error != nil) {
NSLog(#"Error: %#", error.localizedDescription);
}
}
But the NSFetchedResultsController creates a section for each entity. So when I have 200 employees, it creates 200 sections. Why?
And how do I implement those methords properly:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return [[_fetchedResultsController sections] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [[[_fetchedResultsController sections] objectAtIndex:section] numberOfObjects];
}
- (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];
}
// Configure the cell...
cell.textLabel.text = [[_fetchedResultsController objectAtIndexPath:indexPath] valueForKey:#"name"];
return cell;
}
May I do understand the whole concept wrong? Where is the difference between [_fetchedResultsController sections] and [_fetchedResultsController sectionIndexTitles]?
I hope you can help me and thanks in advance!
EDIT: I forget to tell you the most important thing: I want to have sections separated by the first letter of the 'name' attribute. (Like the Music-APP).
Nick
Pass nil as your sectionNameKeyPath when initializing your NSFetchedResultsController.
If you pass name, you basically say "please create one section for each name". When passing nil, you tell it to create only a single section.
You tableview method implementations look right to me.
[_fetchedResultsController sections] gives you an array with objects that you can ask for things like the number of objects in a section. In contrast, [_fetchedResultsController sectionIndexTitles] is mostly so you can tell the NSFetchedResultsController which section titles to use (i.e you would set this to an array with one string for each section). In your case, you can just ignore it.

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

UItableviewcontroller cell text label content not displayed

I ve successfully parsed json file in my app but when i tried to display all it in table view its not getting displayed .here is my code.
NSString *urlstr=[NSString stringWithFormat:#"http://minora.p41techdev.net/portal.php"];
NSURL *url=[NSURL URLWithString:urlstr];
NSData *data =[NSData dataWithContentsOfURL:url];
NSError *error;
NSArray *json=(NSMutableArray*) [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
//NSLog(#"%#",json);
NSDictionary *dict =[NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:#"a title", #"more data",nil] forKeys:[NSArray arrayWithObjects:#"titleKey",#"dataKey", nil]];
NSLog(#"1");
NSString *integ = [dict valueForKey:#"id"];
NSString *title=[dict valueForKey:#"title"];
NSString *category=[dict valueForKey:#"category"];
NSString *description=[dict valueForKey:#"description"];
NSString *spectitle=[dict valueForKey:#"spectitle"];
NSString *specvalue=[dict valueForKey:#"specvalue"];
NSArray *arr =[NSArray arrayWithObjects:integ,title,category,description,spectitle,specvalue, nil];
[tablearray addObject:arr];
NSLog(#"%#",tablearray);
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#pragma mark - Table view data source
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
//#warning Incomplete method implementation.
// Return the number of rows in the section.
return [tablearray count];
NSLog(#"5");
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
cell.textLabel.text=[[tablearray objectAtIndex:indexPath.row] objectAtIndex:1];
return cell;
}
and my json file looks like this
[
{
"id": "1",
"category": "New Category1",
"title": "New Product2",
"description": "Please type a description1",
"imgurl": "http://s.wordpress.org/about/images/wordpress-logo-notext-bg.png",
"spectitle": "Specification",
"specvalue": "Value"
},
{
"id": "2",
"category": "Mobile",
"title": "Samsung",
"description": "Please type a description",
"imgurl": "http://s.wordpress.org/about/images/wordpress-logo-notext-bg.png",
"spectitle": "Price",
"specvalue": "100$"
}
]
Guidance please...
i'm getting thread issue like this
2012-07-20 19:36:03.504 tablevc[2253:f803] 1
2012-07-20 19:36:03.505 tablevc[2253:f803] 2
2012-07-20 19:36:03.507 tablevc[2253:f803] 4
2012-07-20 19:36:03.507 tablevc[2253:f803] 3
2012-07-20 19:36:03.508 tablevc[2253:f803] *** Assertion failure in -[UITableView _createPreparedCellForGlobalRow:withIndexPath:], /SourceCache/UIKit_Sim/UIKit-1914.84/UITableView.m:6061
2012-07-20 19:36:03.508 tablevc[2253:f803] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UITableView dataSource must return a cell from tableView:cellForRowAtIndexPath:'
*** First throw call stack:
(0x13d2022 0x1563cd6 0x137aa48 0x9b32cb 0xb6d28 0xb73ce 0xa2cbd 0xb16f1 0x5ad21 0x13d3e42 0x1d8a679 0x1d94579 0x1d194f7 0x1d1b3f6 0x1d1aad0 0x13a699e 0x133d640 0x13094c6 0x1308d84 0x1308c9b 0x12bb7d8 0x12bb88a 0x1c626 0x2ae2 0x2a55 0x1)
terminate called throwing an exception
I don't see the initialization of tablearray anywhere.
Add this to your viewDidLoad method:
tablearray = [[NSMutableArray alloc] init];
I also see that you're inserting an array within an array. This means that when you need to access the correct data (NSString in your case), you must use the correct index:
cell.textLabel.text=[[tablearray objectAtIndex:indexPath.row] objectAtIndex:1];
I used "objectAtIndex:1" because the title string is stored at index 1 in the inner array. A better, more generic approach would be to use NSDictionary. For example:
NSDictionary *dict =[NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:#"a title", #"more data",nil] forKeys:[NSArray arrayWithObjects:#"titleKey",#"dataKey"]];
Also, make sure your delegate returns the correct amount of sections for your table.
Number of sections in tableview is at least one...
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
//#warning Potentially incomplete method implementation.
// Return the number of sections.
return 1;//It should be at least one.......
}
Write the following code in cellForRowAtIndexPath: method. Otherwise you will get error.
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
I think it will be helpful to you.
call [tableView reloadData] after you add arr in your table array...
[tablearray addObject:arr];
[tableView reloadData]
NSLog(#"2");
hope it helps
You should initialize the cell like this:
- (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];
}
[[cell textLabel] setText:[tablearray objectAtIndex:indexPath.row]];
// Configure the cell...
return cell;
}
You have tablarray with 1 element. This element is an array containing a few entries.
Your data source declares only one cell (tablearray count, which is one).
Your cellForIndexPath method will always look for the first and only the first element in the json array anyway.
Edit: unrelated, but if a field in your json is not set, you'll get nil back, and that'll terminate your array in arrayWithObjects, which will likely cause an out of bounds index down the line.
Be very careful with this method, it's very easy to shoot yourself in the foot with it.