I don't get it. The cellForRowAtIndexPath function's indexPath parameter is always 0. I currently have 7 rows in my table. What my table shows is 7x the first table row. What could cause the indexPath to always be zero?
#implementation SitesViewController
#synthesize sites, siteCell;
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [sites count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"SiteCell"];
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"SiteCell" owner:self options:nil];
cell = siteCell;
self.siteCell = nil;
}
Site *site = [sites objectAtIndex:indexPath.section];
UILabel *siteNameLabel;
siteNameLabel = (UILabel *)[cell viewWithTag:1];
siteNameLabel.text = site.siteName;
return cell;
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSManagedObjectContext *context = [[DigMateAppDelegate sharedAppDelegate] managedObjectContext];
NSEntityDescription *entityDesc = [NSEntityDescription entityForName:#"Sites" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDesc];
NSError *error;
sites = [context executeFetchRequest:request error:&error];
}
Since you have 7 rows and I assume 1 section then you should get record from array based on row index, not section. So the following line in cellForRowAtIndexPath: method:
Site *site = [sites objectAtIndex:indexPath.section];
should be:
// Use indexPath's row instead of section!
Site *site = [sites objectAtIndex:indexPath.row];
Related
Im using the following code to load data into tableview. Following is my code,
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = nil;
cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:#"any-cell"];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:#"any-cell"];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.layer.borderWidth = 1.0;
cell.layer.cornerRadius = 10;
cell.layer.borderColor = [UIColor blackColor].CGColor;
UILabel* productAmountTextLabel = [[UILabel alloc]init];
productAmountTextLabel.font = [UIFont fontWithName:#"HelveticaNeue" size:10];
productAmountTextLabel.frame = CGRectMake(0, 0, 100, 30); // for example
productAmountTextLabel.tag = 10000; // any number
[cell.contentView addSubview:productAmountTextLabel];
}
UILabel* lbl = (UILabel*)[cell.contentView viewWithTag: 10000];
NSManagedObject *device = [self.devices objectAtIndex:indexPath.row];
lbl.text = [device valueForKey:#"amount"];
return cell;
}
The problem is that each cells of tableview displays same value. Why is that so?
Following is my viewdDidLoad method,
- (void)viewDidLoad
{
segmentedControl = [[URBSegmentedControl alloc]initWithTitles:titles icons:icons];
NSError *Error = nil;
APIRequest *apiRequest = [[APIRequest alloc]init];
[apiRequest getPendingData];
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"PendingShipmentDetails"];
self.devices = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"PendingShipmentDetails" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSArray *fetchedObjects = [managedObjectContext executeFetchRequest:fetchRequest error:&Error];
amountArray = [[NSMutableArray alloc] init];
for (NSManagedObjectContext * info in fetchedObjects)
{
[amountArray addObject:[info valueForKey:#"amount"]];
}
segmentedControl.segmentBackgroundColor = [UIColor colorWithRed:86/255.0f green:199/255.0f blue:188/255.0f alpha:1];
[segmentedControl addTarget:self action:#selector(handleSelection:) forControlEvents:UIControlEventValueChanged];
NSLog(#"%#",self.devices);
self.completedOrdersTableView.hidden = YES;
[self.view addSubview:segmentedControl];
[super viewDidLoad];
}
Im fetching values over there and adding it to an array.
within viewDidLoad , it has unique set of datas but within cellForRowAtIndexPath, it has sameset of datas being repeated multiple times.
Thanks to Michaƫl Azevedo for helping me debug the issue. The way I debugged was, I tried logging indexpath.row and indexpath.section. I notices that, row is always 0 and section is dynamic (value changes).
In cellForRowAtIndexPath i was setting the values with reference to indexpath.row which is going to 0 always.
Then i changed my code as follows,
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.devices count];
}
so now the numberOfRowsInSection will not be zero anymore. Hence while accessing it, It wont be fetching the same set of value of multiple times.
I think the problem might be your array named "self.devices". All your label.text is coming from your device model. You can try printing self.devices to take a look at each object in your array.
I experience memory usage increasing each time I scroll up and down my UITableView. I use dequeueReusableCellWithIdentifier, but it doesn't seem it optimizes memory usage. Here's the code:
I thought it was because of UIImageView allocated each time, but when I comment these lines of code and leave only standard UITableViewCell implementation, the problem with memory doesn't go away. Though after leaving the view memory releases (obv it happens only thanks to [self.tableView removeFromSuperview]; method). But while I stay in the view and keep scrolling up and down memory just increases.
#interface ArtistsViewController ()
#property (weak, nonatomic) IBOutlet UITableView *tableView;
#end
#implementation ArtistsViewController
#synthesize fetchedResultsController = _fetchedResultsController;
- (void)viewDidLoad
{
[super viewDidLoad];
NSError *error;
if (![[self fetchedResultsController] performFetch:&error]) {
// Update to handle the error appropriately.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
exit(-1); // Fail
}
self.tableView.delegate = self;
self.tableView.dataSource = self;
self.tableView.backgroundColor = [UIColor colorWithRed:11/255.0 green:12/255.0 blue:20/255.0 alpha:1.0];
self.tableView.opaque = NO;
self.tableView.backgroundView = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:YES];
[self.tableView removeFromSuperview];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
id sectionInfo =
[[_fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
- (void)loadCellData:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
Artist *artist = [_fetchedResultsController objectAtIndexPath:indexPath];
NSLog(#"%#", artist.name);
cell.textLabel.text = [NSString stringWithFormat:#"%#", artist.name];
cell.detailTextLabel.text = [NSString stringWithFormat:#"%lu songs", (unsigned long)[artist.songs count]];
NSString *fileName = [NSString stringWithFormat: #"%#/%#.png", [[NSBundle mainBundle] resourcePath], artist.name];
UIImageView *imgView = [[UIImageView alloc]initWithFrame:CGRectMake(20, 2, 55, 55)];
imgView.image=[UIImage imageWithContentsOfFile:fileName];
[cell.contentView addSubview:imgView];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell) {
NSLog(#"NOCELL");
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
//Load cell data
[self loadCellData:cell atIndexPath:indexPath];
//customization
cell.contentView.backgroundColor = cell.selectedBackgroundView.backgroundColor = [UIColor colorWithRed:11/255.0 green:12/255.0 blue:20/255.0 alpha:1.0];
cell.textLabel.textColor = [UIColor colorWithWhite:222/255.0 alpha:1.0];
cell.detailTextLabel.textColor = [UIColor colorWithRed:62/255.0 green:103/255.0 blue:115/255.0 alpha:1.0];
return cell;
}
#pragma mark - fetchedResultsController
- (NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Artist" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *sort = [[NSSortDescriptor alloc]
initWithKey:#"name" ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
NSFetchedResultsController *theFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil
cacheName:#"Root"];
self.fetchedResultsController = theFetchedResultsController;
_fetchedResultsController.delegate = self;
return _fetchedResultsController;
}
Add Custom Cell instead of using the default cell properties.
static NSString *MyIdentifier = #"MyIdentifier";
MyCustomCell *cell = (MyCustomCell *)[tableView dequeueReusableCellWithIdentifier: MyIdentifier];
if (cell == nil) {
NSArray *nib;
nib = [[NSBundle mainBundle] loadNibNamed:#"MyCustomCell"
owner:self options:nil];
for (id oneObject in nib) if ([oneObject isKindOfClass:[MyCustomCell class]])
cell = (YorBillTableCell *)oneObject;
I know the question of two UITableViews on one UIViewController is a common one but I have not found a solution to one being dependant on the choice of another.
I have a UIViewController with two UITableViews. Table 1 is populated from a CoreData entity with a distinct list of names. When I choose a name from Table 1 I would like Table 2 to be populated with all records related to that person.
The following code works in that Table 1 is populated correctly. However when I choose a name from Table 1 the array based on the selection (which is correct) goes into Table 1 and not Table 2.
I also realise that were a second name to be chosen from Table 1 it will not quite work since it does not distinguish which table has been chosen. Any suggestions here welcome too. I have read that tagging tables is the answer but I have had little success.
Many thanks in advance.
Can anyone help me
- (void)viewDidLoad
{
tableLoad=1;
[tableView setDelegate: self];
[tableView setDataSource: self];
.... code for populating teacherNames with names from Core data
[self.tableView reloadData];
}
-(void) loadSecondTable;
{
[observationTableView setDelegate: self];
[observationTableView setDataSource: self];
tableLoad=2;
[self.tableView reloadData];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (tableLoad==1)
{
return self.teacherNames.count;
}
else
{
return self.observationNames.count;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (tableLoad == 1)
{
static NSString *CellIdentifier = #"teacherCell";
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
NSString *currentList = [[self.teacherNames objectAtIndex:indexPath.row] objectForKey:#"obsTeacherName"];
cell.textLabel.text = currentList;
return cell;
}
else
{
static NSString *CellIdentifier = #"observationCell";
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
Observations *currentList = [self.observationNames objectAtIndex:indexPath.row];
cell.textLabel.text = [NSString stringWithFormat:#"Class %# - %# - %#", currentList.obsClassName, currentList.obsDate, currentList.obsDateTimeStart];
return cell;
}
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if(tableLoad==1)
{
teacherChosenFromTable = [[self.teacherNames objectAtIndex:indexPath.row] objectForKey:#"obsTeacherName"];
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Observations"];
fetchRequest.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"obsTeacherName" ascending:YES]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:#"obsTeacherName == '%#'", teacherChosenFromTable]];
[fetchRequest setPredicate:predicate];
NSError *error = nil;
self.observationNames = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
[self loadSecondTable];
}
else
{
.... load next view based on selection of Table 2
}
}
You don't control the redrawing of tableViews when you scroll it the IOS framework call the DataSource when they need it. The algorithme to populate the data must take into account that. You need to verify which tableView call the delegate.
Try this :
- (void)viewDidLoad
{
[tableView setDelegate: self];
[tableView setDataSource: self];
[observationTableView setDelegate: self];
[observationTableView setDataSource: self];
.... code for populating teacherNames with names from Core data
[self.tableView reloadData];
}
//-(void) loadSecondTable;
//{
// [observationTableView setDelegate: self];
// [observationTableView setDataSource: self];
// [self.tableView reloadData];
//}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
//Caution you name your first tableView tableview and mask the parameter methode you need to rename it
- (NSInteger)tableView:(UITableView *)p_TableView numberOfRowsInSection:(NSInteger)section
{
if (p_tableView == tableView )
{
return self.teacherNames.count;
}
else
{
return self.observationNames.count;
}
}
- (UITableViewCell *)tableView:(UITableView *)p_TableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (p_TableView == tableView )
{
static NSString *CellIdentifier = #"teacherCell";
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
NSString *currentList = [[self.teacherNames objectAtIndex:indexPath.row] objectForKey:#"obsTeacherName"];
cell.textLabel.text = currentList;
return cell;
}
else
{
static NSString *CellIdentifier = #"observationCell";
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
Observations *currentList = [self.observationNames objectAtIndex:indexPath.row];
cell.textLabel.text = [NSString stringWithFormat:#"Class %# - %# - %#", currentList.obsClassName, currentList.obsDate, currentList.obsDateTimeStart];
return cell;
}
}
- (void)tableView:(UITableView *)p_TableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (p_TableView == tableView )
{
teacherChosenFromTable = [[self.teacherNames objectAtIndex:indexPath.row] objectForKey:#"obsTeacherName"];
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Observations"];
fetchRequest.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"obsTeacherName" ascending:YES]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:#"obsTeacherName == '%#'", teacherChosenFromTable]];
[fetchRequest setPredicate:predicate];
NSError *error = nil;
self.observationNames = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
[self.tableView reloadData];
}
else
{
.... load next view based on selection of Table 2
}
}
Don't do tagging. The methods are called on the same thread but there's no way in general to control the state of tableLoad correctly. All datasource methods have a tableView as an argument, compare the argument value with the values of linked outlets or variables where you should save the references to your table views after they are initialized.
Distinguish the table view where the cell is selected in the same way.
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.
I am having problems passing data between view controllers.
All three view controllers are table views.
WorkoutTypeVC
WorkoutSetVC
WorkoutExerciseVC
I have three entities,
WorkoutType
workouts(->WorkoutSet) One to Many
WorkoutSet
exercises(->WorkoutExercise) One to Many
workoutType(->WorkoutType) Inverse
WorkoutExercise
workoutSet(->WorkoutSet) Inverse
I am able to switch between all three view controllers, WorkoutTypeVC loads correctly showing all entries, When selected WorkoutSetVC is loaded showing the correct entries corresponding to the selection made from WorkoutTypeVC.
But when i select an entry from WorkoutSetVC, WorkoutExerciseVC loads but is empty, Even the title of the selection doesn't load.
I have used the same code which i used when switching from WorkoutTypeVC and WorkoutSetVC.
Below is the code for switching views in WorkoutType.m file:
-(void)fetchWorkoutTypes
{
NSFetchRequest *fetchRequest =
[NSFetchRequest fetchRequestWithEntityName:#"WorkoutType"];
NSString *cacheName = [#"WorkoutType" stringByAppendingString:#"Cache"];
NSSortDescriptor *sortDescriptor =
[NSSortDescriptor sortDescriptorWithKey:#"workoutType" ascending:YES];
[fetchRequest setSortDescriptors:#[sortDescriptor]];
self.fetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil cacheName:cacheName];
NSError *error;
if (![self.fetchedResultsController performFetch:&error])
{
NSLog(#"Fetch failed: %#", error);
}
}
- (void)viewDidAppear:(BOOL)animated{
[self fetchWorkoutTypes];
[self.tableView reloadData];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return self.fetchedResultsController.fetchedObjects.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell =
[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1
reuseIdentifier:CellIdentifier];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
WorkoutType *workoutType = (WorkoutType *)[self.fetchedResultsController
objectAtIndexPath:indexPath];
cell.textLabel.text = workoutType.workoutType;
cell.detailTextLabel.text = [NSString stringWithFormat:#"(%d)", workoutType.workouts.count];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
WorkoutType *workoutType = (WorkoutType *)[self.fetchedResultsController objectAtIndexPath:indexPath];
WorkoutSetViewController *detailViewController = [[WorkoutSetViewController alloc] initWithWorkoutType:workoutType];
[self.navigationController pushViewController:detailViewController animated:YES];
}
Below is the code for WorkoutSetVC.m
-(void)fetchWorkoutSets
{
NSFetchRequest *fetchRequest =
[NSFetchRequest fetchRequestWithEntityName:#"WorkoutSet"];
NSString *cacheName = [#"WorkoutSet" stringByAppendingString:#"Cache"];
NSSortDescriptor *sortDescriptor =
[NSSortDescriptor sortDescriptorWithKey:#"workoutName" ascending:YES];
[fetchRequest setSortDescriptors:#[sortDescriptor]];
self.fetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil cacheName:cacheName];
NSError *error;
if (![self.fetchedResultsController performFetch:&error])
{
NSLog(#"Fetch failed: %#", error);
}
}
- (id)initWithWorkoutType:(WorkoutType *)workoutType
{
self = [super initWithStyle:UITableViewStylePlain];
if (self)
{
self.workoutType = workoutType;
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.title = self.workoutType.workoutType;
[self fetchWorkoutSets];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return self.workoutType.workouts.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];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
WorkoutSet *workoutSet = [self.workoutType.workouts.allObjects objectAtIndex:indexPath.row];
cell.textLabel.text = workoutSet.workoutName;
cell.detailTextLabel.text = [NSString stringWithFormat:#"(%d)", workoutSet.exercises.count];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
WorkoutSet *workoutSet = (WorkoutSet *)[self.fetchedResultsController objectAtIndexPath:indexPath];
WorkoutExerciseTableViewController *detailViewController = [[WorkoutExerciseTableViewController alloc] initWithWorkoutSet:workoutSet];
[self.navigationController pushViewController:detailViewController animated:YES];
}
Below is the code for WorkoutExercise.m
- (id)initWithWorkoutSet:(WorkoutSet *)workoutSet
{
self = [super initWithStyle:UITableViewStylePlain];
if (self)
{
self.workoutSet = workoutSet;
}
return self;
}
-(void)fetchWorkoutExercises
{
NSFetchRequest *fetchRequest =
[NSFetchRequest fetchRequestWithEntityName:#"WorkoutExercise"];
NSString *cacheName = [#"WorkoutExercise" stringByAppendingString:#"Cache"];
NSSortDescriptor *sortDescriptor =
[NSSortDescriptor sortDescriptorWithKey:#"exerciseName" ascending:YES];
[fetchRequest setSortDescriptors:#[sortDescriptor]];
self.fetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil cacheName:cacheName];
NSError *error;
if (![self.fetchedResultsController performFetch:&error])
{
NSLog(#"Fetch failed: %#", error);
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.title = self.workoutSet.workoutName;
[self fetchWorkoutExercises];
}
- (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];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
WorkoutExercise *exercise = [self.workoutSet.exercises.allObjects objectAtIndex:indexPath.row];
cell.textLabel.text = exercise.exerciseName;
return cell;
}
Not sure as to what i need to do for the third view controller to list all the entries, Even the title for the third view controller doesn't load which is coded in the ViewDidLoad Method.
Thank You
The problem is (I assume) the inconsistent use of data sources in the second (and third?)
view controller.
In your WorkoutSetViewController, cellForRowAtIndexPath accesses the objects directly via self.workoutType.workouts.allObjects, but didSelectRowAtIndexPath uses a fetched results controller (FRC). This does not make sense. If the table view is driven by a FRC, all data source methods must use the FRC.
Perhaps self.fetchedResultsController is nil in the second view controller?
Then the workoutSet passed to the third view controller would be nil, which would
explain that no title is set and no objects are displayed.
And generating an array from self.workoutType.workouts with allObjects is also problematic, because the order of the array elements can be random.
The second view controller should use a fetched results controller to display
all WorkoutSet objects related to the given workoutType.
And the third view controller should use a fetched results controller to display
all WorkoutExercise objects related to the given workoutSet.
UPDATE: fetchWorkoutSets in WorkoutSetVC.m should look like this:
-(void)fetchWorkoutSets
{
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"WorkoutSet"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"workoutType = %#", self.workoutType];
[fetchRequest setPredicate:predicate];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"workoutName" ascending:YES];
[fetchRequest setSortDescriptors:#[sortDescriptor]];
self.fetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil cacheName:nil];
self.fetchedResultsController.delegate = self;
NSError *error;
if (![self.fetchedResultsController performFetch:&error])
{
NSLog(#"Fetch failed: %#", error);
}
}
The predicate is important to fetch only workout sets that are related to self.workoutType.
And similarly, fetchWorkoutExercises in WorkoutExercise.m would use the predicate
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"workoutSet = %#", self.workoutSet];
[fetchRequest setPredicate:predicate];
to fetch only exercises that are related to self.workoutSet.
For the data source methods, have a look at the NSFetchedResultsController documentation, it contains sample code that you can copy
and adapt to your needs. Or you create a fresh Xcode iOS application with the "Master-Detail Application" template and select the "Core Data" checkbox. That will also give you
sample code.
For example, in cellForRowAtIndexPath in WorkoutSetVC.m you would replace
WorkoutSet *workoutSet = [self.workoutType.workouts.allObjects objectAtIndex:indexPath.row];
by
WorkoutSet *workoutSet = [self.fetchedResultsController objectAtIndexPath:indexPath];