in my app the user adds cells to a tableView using coreData. This works quite well. But now I want the table view to have sections.
The viewController in which you add new cells look like:
#property (strong) NSManagedObject *travel;
...
-(void)viewDidLoad{
countryName = [[NSArray alloc] initWithObjects:
#"USA", #"England", #"Italy", nil];
countryLabel.text= [countryName objectAtIndex:[picker selectedRowInComponent:0]];
}
- (IBAction)save:(id)sender {
[self.travel setValue:countryLabel.text forKey:#"country"];
}
and in the viewController which displays the cells in a tableView:
#property (strong) NSMutableArray *travelAll;
...
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSPredicate *predicate=[NSPredicate predicateWithFormat:#"position == %#",_positionString];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Travel"];
[fetchRequest setPredicate : predicate ];
self.travelAll = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];
[self.tableView reloadData];
...
- (NSString *)tableView:(UITableView *)tableView
titleForHeaderInSection:(NSInteger)section
{ NSArray* headers = [NSArray arrayWithObjects:#"USA",#"England","Italy",nil];
return [headers objectAtIndex:section];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 3;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.travelAll count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
NSManagedObject *travel = [self.travelAll objectAtIndex:indexPath.row];
...
return cell;
}
But now my tableView I have just this three sections (headers) but I can't add cell to them.
For example: the user selects USA for his new cell, so this cell should be displayed in the section USA
Your datasource methods are flawed. For example, you are returning the same cell data for each section. There are other problems as well.
It is much better to use a NSFetchedResultsController. Start from the Apple templates (you get that if you create a new project and choose "Use Core Data"), that employ the fetched results controller.
Your section design becomes very simple then: it is enough to simply specify the sectionNameKeyPath property.
Related
i use below code to binding tableview cell by "contacts" entity
- (NSManagedObjectContext *)managedObjectContext
{
NSManagedObjectContext *context=nil;
id delegate=[[UIApplication sharedApplication]delegate];
if ([delegate performSelector:#selector(managedObjectContext)])
{
context=[delegate managedObjectContext];
}
return context;
}
- (void)viewDidAppear:(BOOL)animated
{
//fetching contact into tableview
NSManagedObjectContext *moc=[self managedObjectContext];
totalFetch=[[NSFetchRequest alloc]initWithEntityName:#"Contacts"];
// 4 - Sort it if you want
totalFetch.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"name" ascending:YES selector:#selector(localizedCaseInsensitiveCompare:)]];
_contacts=[[moc executeFetchRequest:totalFetch error:nil]mutableCopy];
[self.myTable reloadData];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//create and initializetion cell
static NSString *cellIdentifier=#"cell";
UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
NSManagedObject *contact=[_contacts objectAtIndex:indexPath.row];
//implementing tableViewCells with Contact attribs
[cell.textLabel setText:[NSString stringWithFormat:#"%#",[contact valueForKey:#"name"]]];
[cell.detailTextLabel setText:[contact valueForKey:#"phoneNum"]];
UIImage *image = [UIImage imageWithData:[contact valueForKey:#"photo"]];
[cell.imageView setImage:image];
return cell;
}
now i want to add index and section to mytableview by first letter of "name" field in "contacts" entity, Please put your answers according to my code, thanks
You need to implement the following datasource methods for UITableView:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView
The first two are standard and implement the cells that are going to be displayed in the table view.
The third one is the number of sections the table view should be divided up into. For something like this I would use an 'alphabet array' i.e. an array with all the letters of the alphabet in e.g.:
NSMutableArray *alphabetArray = [[NSMutableArray alloc] init];
for (int i = 0; i < 26; i++) {
char letter = 'A';
letter += i;
[alphabetArray addObject:[NSString stringWithFormat:#"%c", letter]];
}
You can return alphabetArray.count for the third datasource method.
The fourth method requests the section number for the section title, all you need to do here is return: [alphabetArray indexOfObject:title];.
The fifth method is asking for the title for a specific section so you can return: alphabetArray[section]; here.
Finally the sixth method is asking for all the titles in an array so you can just return alphabetArray here .
In order to get the number of rows in a section you need to break down your contacts array by starting letter. To do this you can use predicates e.g.
NSString *letter = alphabetArray[indexPath.section];
NSArray *filteredContacts = [contacts filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"name BEGINSWITH[cd] %#", letter]];
return filteredContacts.count;
You can apply the same logic to get the contact to display in a cell:
NSString *letter = alphabetArray[indexPath.section];
NSArray *filteredContacts = [contacts filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"name BEGINSWITH[cd] %#", letter]];
Contact *contact = filteredContacts[indexPath.row];
Hope this helps.
I have been trying to set the uiTableViewHeader for my UITableView for a couple of days now with no luck. I don't think I am far off. Currently it shows the section Titles however multiples the number of records by X amount ( I presume my count may be wrong).
I think I need to further configure my cellForRowAtIndexPath method but Im unsure how.
I am a bit confused. I need to group the rowsAtIndexPath to the sections and stop them multiplying.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"atozCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
//Searchbar code is here
NSDictionary *dataDict = [self.sortedArray objectAtIndex:indexPath.section];
cell.textLabel.text = [dataDict objectForKey:#"Title"];
}
return cell;
}
Section Counts
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [self.sectionArray count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
return [self.sectionArray objectAtIndex:section];
}
Data Populated from
// Find out the path of recipes.plist
NSString *path = [[NSBundle mainBundle] pathForResource:#"law2" ofType:#"plist"];
// Load the file content and read the data into arrays
self.dataArray = [NSArray arrayWithContentsOfFile:path];
//Sort the array by section
self.sortedArray = [self.dataArray sortedArrayUsingDescriptors:#[
[NSSortDescriptor sortDescriptorWithKey:#"Section" ascending:YES],
[NSSortDescriptor sortDescriptorWithKey:#"Title" ascending:YES]]];
//Section for sorting
self.sectionArray = [self.sortedArray valueForKeyPath:#"Section"];
Always you are sending index of the object. So please try to use this one
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index
{
return [self.tableDataIndexTitles objectAtIndex:index];
}
I created a table with sections. Each section has a date (2014-03-23) as a title and under each date I want to populate a list of games to be played that day. When I run the app the table gets the section title fine (date of game), but every section has the same matches list. I want to have the games match under the section date.
I know i need to include indexPath.section in the CellForRowsAtIndexPath but I'm having a hard time figuring it out.
Here's my code:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return gamesArray.count;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
uniqueArray = [[NSOrderedSet orderedSetWithArray:dateSection] array];
return [uniqueArray count];
}
-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
return [self.dateSection objectAtIndex:section];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//NSString *CellIdentifier = [NSString stringWithFormat:#"games cell-%ld-%ld", (long)indexPath.section, (long)indexPath.row];
static NSString *CellIdentifier = #"games cell";
//NSString *CellIdentifier = [NSString stringWithFormat:#"cell-%d-%d", indexPath.section, indexPath.row];
CustomInboxCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = (CustomInboxCell *)[[CustomInboxCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
PFObject *post = [gamesArray objectAtIndex:indexPath.row];
[cell.teamsLabel setText:[post objectForKey:#"teams"]];
[cell.liveRepeatLabel setText:[post objectForKey:#"liveRepeat"]];
[cell.gameTimeLabel setText:[post objectForKey:#"gameTime"]];
return cell;
}
Any help would be greatly appreciated.
//======================================================
//I decided to use a predicate to filter and get the number of items per date(Number of games per date)
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSString *sectionTitle = [uniqueArray objectAtIndex:section];
if (section >=0) {
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"gameDate == %#",sectionTitle];
NSLog(#"section name = %#", sectionTitle);
NSArray *filtered = [gamesArray filteredArrayUsingPredicate:predicate];
NSLog(#"filtered = %#",filtered);
return filtered.count;
}
return 0;
}
//I just need to iterate through every date and return the number of games per date. Any suggestions?
You need a separate array for each section of the table. In numberOfRowsForSection, you need to return a count for the array that corresponds to the given section.
Here's an example. The data for the table is stored in an NSArray called tableData. The array has one entry for each section of the table. Each entry in tableData is an NSDictionary. The NSDictionary has two keys, title and items. The title key corresponds to an NSString that serves as the title for the table section. The items key corresponds to an NSArray that has the row information for the table section.
The table is organized into two sections like this
Fruits
Apples
Oranges
Animals
Dog
Cat
Horse
Cow
Here's the code
#import "MainViewController.h"
#interface MainViewController () <UITableViewDataSource, UITableViewDelegate>
#property (weak, nonatomic) IBOutlet UITableView *tableView;
#property (strong, nonatomic) NSArray *tableData;
#end
#implementation MainViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.tableView.dataSource = self;
self.tableView.delegate = self;
NSDictionary *fruits, *animals;
fruits = #{ #"title" : #"Fruits" , #"items" : #[#"Apples", #"Oranges"] };
animals = #{ #"title" : #"Animals", #"items" : #[#"Dog", #"Cat", #"Horse", #"Cow"] };
self.tableData = #[fruits, animals];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return( self.tableData.count );
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSDictionary *sectionData = self.tableData[section];
NSArray *items = sectionData[#"items"];
return( items.count );
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
NSDictionary *sectionData = self.tableData[section];
NSString *title = sectionData[#"title"];
return( title );
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"SomeCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
NSDictionary *sectionData = self.tableData[indexPath.section];
NSArray *items = sectionData[#"items"];
NSString *name = items[indexPath.row];
cell.textLabel.text = name;
return cell;
}
#end
This is a data set problem, you need to have a separate dataset prepared for each section of your tableview and iterate them using the index path (row, section) properties in your cellForRowAtIndexPath method. If you can do NSLog and share your dataset, it would be more helpful to answer accurately. Hope this helps.
I have successfully stored address book data in Core Data but i m not able to retrieve it and display it in tableView .What am i missing ?
This is how i fetch data from core data.
-(void)fetchFromDatabase
{
AddressBookAppDelegate *appDelegate =[[UIApplication sharedApplication]delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSEntityDescription *entityDesc = [NSEntityDescription entityForName:#"AddressBook" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDesc];
NSError *error;
self.arrayForTable = [context executeFetchRequest:request error:&error];
NSLog(#"fetched data = %#",[self.arrayForTable lastObject]); //this shows the data
[self.tableView reloadData];
And this is my table view configuration.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.arrayForTable 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];
if ([self.arrayForTable count]>0)
{
NSLog(#" table view content = %#",[self.arrayForTable lastObject]);// this doesn't log
AddressBook *info = [self.arrayForTable objectAtIndex:indexPath.row];
cell.textLabel.text = info.firstName;
}
}
return cell;
}
As it turned out in the discussion, self.arrayForTable was replaced by an empty array in viewWillAppear after fetching the objects in fetchFromDatabase.
Another problem was that each run of the program created new objects in the database,
leading to duplicate objects. You can either
delete all objects before inserting the new objects, or
for each name, check if a matching object already exists in the database, and insert a new one only if necessary.
More advanced techniques are described in "Implementing Find-or-Create Efficiently" in the "Core Data Programming Guide".
I have set up a UIViewController with a table view and cell added. The delegate and data source are linked to the table view. However I getting this error and I am confused as to why. The code works when I use it with a UITableViewController but not with a UIViewcontroller. The reason for wanting to use a UIViewController is that the navigation bar scrolls with the table when using a UITableViewController and I was advised on here to use a UIViewController and add the Table view.
#import "ObViewControllerObservationsTable.h"
#import "ObAppDelegate.h"
#import "Observations.h"
#import "ObViewControllerTableDetail.h"
#import "ObViewControllerMainMenu.h"
#interface ObViewControllerObservationsTable ()
#property (nonatomic, strong) NSArray *teacherNames;
#property (nonatomic, readonly) NSManagedObjectContext *managedObjectContext;
#property (weak, nonatomic) IBOutlet UIBarButtonItem *detailsButton;
#end
#implementation ObViewControllerObservationsTable
- (void)viewDidLoad
{
[super viewDidLoad];
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Observations"];
fetchRequest.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"obsTeacherName" ascending:YES]];
NSError *error = nil;
self.teacherNames = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
[self.tableView reloadData];
}
- (NSManagedObjectContext *) managedObjectContext
{
return [(ObAppDelegate *) [[UIApplication sharedApplication] delegate] managedObjectContext];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.teacherNames.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"teacherCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
Observations *currentList = [self.teacherNames objectAtIndex:indexPath.row];
cell.textLabel.text = currentList.obsID;
return cell;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
[self.managedObjectContext deleteObject:[self.teacherNames objectAtIndex:indexPath.row]];
[self.managedObjectContext save:nil];
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Observations"];
fetchRequest.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"obsTeacherName" ascending:YES]];
NSError *error = nil;
self.teacherNames = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
if (self.teacherNames.count == 0)
{
self.detailsButton.enabled = NO;
}
else
{
self.detailsButton.enabled = YES;
}
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
else if (editingStyle == UITableViewCellEditingStyleInsert) {
}
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
obsID = [[self.teacherNames objectAtIndex:indexPath.row] obsID];
self.detailsButton.enabled = YES;
}
dequeueReusableCellWithIdentifier: returns nil if no reusable object exists in the reusable-cell queue. (See UITableView reference).
Here is how it should be:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"teacherCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
// More initializations if needed.
}
Observations *currentList = [self.teacherNames objectAtIndex:indexPath.row];
cell.textLabel.text = currentList.obsID;
return cell;
}
I think the problem is the dequeueReusableCellWithIdentifier method. I tried a quick sample project with a tableView in a view controller rather than in a UITableViewController and encountered the error you describe. When I manually generated the cell using
[UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"testCell"];
it worked fine.