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.
Related
I'm attempting to build a game scoring app that utilizes a custom table cell with player photos, names, buttons etc... There are add/subtract buttons directly in the custom cell of the tableview that are hitting my save method, and it's storing it back in Core Data for that specific user.
The problem is with the on-screen score not updating and reflecting the change. After the save action to Core Data is complete, I'm calling the [self.tableView reloadData];... nothing. However, if I restart the app, then the change in score (for any of the players I've clicked on), appears.
Maybe I'm making this harder than it needs to be, either that, or I'm just not grasping the real problem.
Thoughts / comments?
Thanks a load in advance.
:-)
Sorry if this is overkill, but here is the majority of my implementation file:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self resetViews];
}
- (void)viewDidLoad {
[super viewDidLoad];
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
[context setUndoManager:nil];
_managedObjectContext = context;
self.tableView.delegate = self;
[self setNeedsStatusBarAppearanceUpdate];
}
-(void)resetViews {
NSLog(#"\n\n\nresetViews()");
[self setupFetchedResultsController];
[self.tableView reloadData];
[self.view setNeedsDisplay];
}
- (void)setupFetchedResultsController {
NSString *entityName = #"Players";
NSLog(#"Setting up a Fetched Results Controller for the Entity named %#", entityName);
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:entityName];
request.sortDescriptors = [NSArray arrayWithObject:
[NSSortDescriptor
sortDescriptorWithKey:#"playerName"
ascending:YES
selector:#selector(localizedCaseInsensitiveCompare:)]];
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil
cacheName:nil];
NSError *error;
NSArray *results = [_managedObjectContext executeFetchRequest:request error:&error];
_playerArray = [[NSMutableArray alloc]initWithArray:results];
NSLog(#"_playerArray count: %i", [_playerArray count]);
NSLog(#"\n");
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return _playerArray.count;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"playerCell";
ScoringCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
// Configure the cell...
Players *player_info = [_playerArray objectAtIndex:indexPath.row];
NSSet *score = player_info.scores;
for (Scoring *perObj in score){
cell.lblPlayerScore.text = [perObj.score stringValue];
NSLog(#"\n\n\n score for %#: %#", player_info.playerName, perObj.score);
}
cell.lblPlayerName.text = player_info.playerName;
cell.lblPlayerNickName.text = player_info.playerNickName;
cell.btnIncreaseScore.tag = indexPath.row;
cell.btnDecreaseScore.tag = indexPath.row;
cell.imgPlayerPhoto.image = [UIImage imageNamed:#"tmp_playerImage"];
return cell;
}
- (IBAction)increaseScore:(id)sender {
NSLog(#"PageContentViewController: increaseScore()");
UIButton* btn=(UIButton*)sender;
int selectedPlayerInt = btn.tag;
//NSLog(#"Selected row is: %d",btn.tag);
Players *player_info = [_playerArray objectAtIndex:selectedPlayerInt];
[self updateRowScore:player_info:#"add"];
}
- (IBAction)decreaseScore:(id)sender {
NSLog(#"PageContentView: decreaseScore()");
UIButton* btn=(UIButton*)sender;
int selectedPlayerInt = btn.tag;
//NSLog(#"Selected row is: %d",btn.tag);
Players *player_info = [_playerArray objectAtIndex:selectedPlayerInt];
[self updateRowScore:player_info:#"subtract"];
}
-(void)updateRowScore: (Players *)player_info :(NSString *)modifier {
NSLog(#"\n\nupdateRowScore()");
NSLog(#"Update score (%#) for: %#\n", modifier, player_info.playerName);
NSArray *scoreDataArray;
if ([self playerScoreCount:player_info] == 0) {
// NEW score... we've never scored before.
Scoring *scoring_data = [NSEntityDescription
insertNewObjectForEntityForName:#"Scoring"
inManagedObjectContext:_managedObjectContext];
//Since this is the first score, always set it to 1
scoring_data.score = [NSNumber numberWithInt:1];
scoring_data.holeNumber = [NSNumber numberWithInt:_pageIndex];
scoring_data.scoredBy = player_info;
} else {
//Update existing player score..
NSError *error = nil;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *BEntity = [NSEntityDescription entityForName:#"Scoring" inManagedObjectContext:_managedObjectContext];
[fetchRequest setEntity:BEntity];
NSPredicate *predicate = [NSPredicate
predicateWithFormat:#"(scoredBy = %#)", [player_info objectID]];
[fetchRequest setPredicate:predicate];
NSArray *results = [_managedObjectContext executeFetchRequest:fetchRequest error:&error];
scoreDataArray = [[NSMutableArray alloc]initWithArray:results];
Scoring *score_update = [scoreDataArray objectAtIndex:0];
int currentScore = [score_update.score intValue];
NSLog(#"current score: %d", currentScore);
if ([modifier isEqual: #"add"]) {
currentScore++;
} else {
// Don't allow negative scores.
if (currentScore >= 1) {
currentScore--;
} else {
currentScore = 0;
}
}
NSLog(#"NEW score: %d", currentScore);
score_update.score = [NSNumber numberWithInt:currentScore];
}
// write to database
[self.managedObjectContext save:nil];
[self resetViews];
}
UPDATE:
Thanks for the tip bbarnhart... I had read through that post before and had used that for a basis from which I had started. Decided to take it a step further and refactor a chunk of code using more of the Ray Wenderlich example.
I've seen some improvements to what's being recorded, and reported back through the NSLog's... but the view just still is not changing.
The action is increasing the score, and then I'm resetting the cell using [self configureCell:cell atIndexPath:path]; In there... the method that is responsible for sending text to the display... the NSLog is showing 2014-12-04 22:40:40.199 appName[7153:150248] Score for Tim: 4 when the display still only shows 3.
I know this is some stupid rookie move... I'm just doing something dead wrong that I can't figure out. Here's a snippet of the amended code.
- (NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Players"
inManagedObjectContext:_managedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *sort = [[NSSortDescriptor alloc]
initWithKey:#"playerName" ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
NSFetchedResultsController *theFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:_managedObjectContext
sectionNameKeyPath:nil
cacheName:#"Root"];
self.fetchedResultsController = theFetchedResultsController;
_fetchedResultsController.delegate = self;
NSError *error;
NSArray *results = [_managedObjectContext executeFetchRequest:fetchRequest error:&error];
_playerArray = [[NSMutableArray alloc]initWithArray:results];
NSLog(#"_playerArray count: %i", [_playerArray count]);
return _fetchedResultsController;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
id sectionInfo = [[_fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"playerCell";
ScoringCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell) {
cell = [[ScoringCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:cellIdentifier];
}
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
- (void)configureCell:(ScoringCell *)cell atIndexPath:(NSIndexPath *)indexPath {
Players *player_info = [_fetchedResultsController objectAtIndexPath:indexPath];
NSSet *scoreSet = player_info.scores;
NSString *cell_score;
for (Scoring *scoreObj in scoreSet) {
cell_score = [scoreObj.score stringValue];
}
NSLog(#"Score for %#: %#", player_info.playerName, cell_score);
if (cell_score != nil) {
cell.lblPlayerScore.text = cell_score;
}
cell.lblPlayerName.text = player_info.playerName;
cell.lblPlayerNickName.text = player_info.playerNickName;
cell.btnIncreaseScore.tag = indexPath.row;
cell.btnDecreaseScore.tag = indexPath.row;
cell.imgPlayerPhoto.image = [UIImage imageNamed:#"demo_playerb"];
[self resetViews];
NSLog(#"\n");
}
- (IBAction)increaseScore:(id)sender {
NSLog(#"PageContentViewController: increaseScore()");
UIButton *senderButton = (UIButton *)sender;
int selectedPlayerInt = senderButton.tag;
NSIndexPath *path = [NSIndexPath indexPathForRow:senderButton.tag inSection:0];
Players *player_info = [_playerArray objectAtIndex:selectedPlayerInt];
[self updateRowScore:player_info:#"add":selectedPlayerInt:path];
}
-(void)updateRowScore:(Players *)player_info :(NSString *)modifier :(int)selectedPlayerInt :(NSIndexPath *)path {
NSArray *scoreDataArray;
if ([self playerScoreCount:player_info] == 0) {
// NEW score... we've never scored before.
Scoring *scoring_data = [NSEntityDescription
insertNewObjectForEntityForName:#"Scoring"
inManagedObjectContext:_managedObjectContext];
//Since this is the first score, always set it to 1
scoring_data.score = [NSNumber numberWithInt:1];
scoring_data.holeNumber = [NSNumber numberWithInt:_pageIndex];
scoring_data.scoredBy = player_info;
} else {
//Update existing player score..
NSError *error = nil;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *BEntity = [NSEntityDescription entityForName:#"Scoring"
inManagedObjectContext:_managedObjectContext];
[fetchRequest setEntity:BEntity];
NSPredicate *predicate = [NSPredicate
predicateWithFormat:#"(scoredBy = %#)", [player_info objectID]];
[fetchRequest setPredicate:predicate];
NSArray *results = [_managedObjectContext executeFetchRequest:fetchRequest error:&error];
scoreDataArray = [[NSMutableArray alloc]initWithArray:results];
Scoring *score_update = [scoreDataArray objectAtIndex:0];
int currentScore = [score_update.score intValue];
NSLog(#"current score: %d", currentScore);
if ([modifier isEqual: #"add"]) {
currentScore++;
} else {
// Don't allow negative scores.
if (currentScore >= 1) {
currentScore--;
} else {
currentScore = 0;
}
}
NSLog(#"NEW score: %d", currentScore);
score_update.score = [NSNumber numberWithInt:currentScore];
}
// write to database
[self.managedObjectContext save:nil];
static NSString *cellIdentifier = #"playerCell";
ScoringCell *cell = [_tableView dequeueReusableCellWithIdentifier:cellIdentifier];
[self configureCell:cell atIndexPath:path];
[self resetViews];
}
----------
UPDATE:
Been awhile since I've had a chance to revisit, and just noticed a new problem since enabling your tips. When scrolling down or up in the list and pulling beyond the normal boundaries, the tableview data seems to overwrite the display for the row either above or below the current line. Weird... Not sure if this animated Gif will show up in Stack. Here's an example:
The main reason your table view is not updating dynamically is NSFetchedResultsController uses a delegate for notification when changes occur. You'll need to set that delegate, self.fetchedResultsController.delegate = self and then add the delegate methods.
Here is a link to an example for managing a UITableView with a NSFetchedResultsController.
Update
Implement these NSFetchResultsController delegate methods to allow your table to be dynamically updated.
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath: (NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id )sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
Generally, these methods contain boilerplate code for updating your table which you will also find in the link above.
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 am looking how to make a sum of all the cells in my tableview.
Each cell crated by user have a number on a label. This number is diferent in each cell.
How can i make the sum of all the numbers in all the cells?
I think its important to say im using Core Data.
Thanks, hope anyone can help me.
Be free to ask any other detail.
EDIT:
#synthesize fetchedResultsController = __fetchedResultsController;
#synthesize managedObjectContext = __managedObjectContext;
#synthesize selectedYear;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"fondo.png"]];
self.navigationItem.leftBarButtonItem = self.editButtonItem;
[self.tableView setSeparatorColor:[UIColor brownColor]];
}
- (void)setupFetchedResultsController
{
// 1 - Decide what Entity you want
NSString *entityName = #"Years"; // Put your entity name here
NSLog(#"Setting up a Fetched Results Controller for the Entity named %#", entityName);
// 2 - Request that Entity
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:entityName];
// 3 - Filter it if you want
//request.predicate = [NSPredicate predicateWithFormat:#"Years.name = Blah"];
// 4 - Sort it if you want
request.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"subject"
ascending:YES
selector:#selector(localizedCaseInsensitiveCompare:)]];
// 5 - Fetch it
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil
cacheName:nil];
[self performFetch];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self setupFetchedResultsController];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"My Cell";
MarksCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[MarksCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
Year *years = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.celltitlelabel.text = years.subject;
return cell;
}
Assuming that the Year entity has an attribute value which is an NSNumber:
NSArray *myNumbers = self.fetchedResultsController.fetchedObjects;
NSNumber *sum = [myNumbers valueForKeyPath:#"#sum.value"];
I subclassed UITableViewCell. Basically what happens is that when you press the UITableViewCell drawing on the cell's layer occurs causing the cell to appear different. However, when I delete a cell, that drawing drops to the cell below it. This to me seems to indicate that the cell's format is getting re-used as would be normal. Thus I redrew the cell in CellForRowAtIndexPath as one can see below...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
AGProgressViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
cell.textLabel.backgroundColor = [UIColor clearColor];
cell.detailTextLabel.backgroundColor = [UIColor clearColor];
//NSLog(#"progress value = %f", [cell.progress floatValue]);
if (!cell) {
cell = [[AGProgressViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
Task * task = nil;
if (indexPath.section == 0){
task = [self.tasksByDay[#"anyday"] objectAtIndex:indexPath.row];
} else if (indexPath.section == 1){
task = [self.tasksByDay[#"monday"] objectAtIndex:indexPath.row];
} else if (indexPath.section == 2){
task = [self.tasksByDay[#"tuesday"] objectAtIndex:indexPath.row];
} else if (indexPath.section == 3){
task = [self.tasksByDay[#"wednesday"] objectAtIndex:indexPath.row];
} else if (indexPath.section == 4){
task = [self.tasksByDay[#"thursday"] objectAtIndex:indexPath.row];
} else if (indexPath.section == 5){
task = [self.tasksByDay[#"friday"] objectAtIndex:indexPath.row];
} else if (indexPath.section == 6){
task = [self.tasksByDay[#"saturday"] objectAtIndex:indexPath.row];
} else if (indexPath.section == 7){
task = [self.tasksByDay[#"sunday"] objectAtIndex:indexPath.row];
}
cell.progress = [NSNumber numberWithFloat:([task.timeSpent floatValue]/[task.time floatValue])];
// this is calling the redrawing method in the cell
[cell drawFillInAtPercent:[task.timeSpent floatValue]/[task.time floatValue]];
//NSLog(#"progress value = %f vs. time spent = %f", [cell.progress floatValue], [task.timeSpent floatValue]/[task.time floatValue]);
cell.textLabel.text = task.name;
cell.detailTextLabel.text = [NSString stringWithFormat:#"%d minutes",
[task.time intValue] - [task.timeSpent intValue]];
return cell;
}
However, this did not fix the problem. All those NSLogs showed the cell being at the right level for every re-drawing. This means, for some reason, the cell that is getting deleted is not getting called in the cellForRowAtIndex path. The weird thing is that the text labels are changing, simply the custom drawing that I do in the UITableViewCell subclass is not changing.
This is the method that I am calling there in the subclass.
-(void) drawFillInAtPercent: (float) percent{
//if (percent > 0){
NSLog(#"progress layer at percent %f", percent);
_progressLayer = [CAGradientLayer layer];
_progressLayer.frame = CGRectMake(self.bounds.origin.x,
self.bounds.origin.y,
self.bounds.size.width * percent,
self.bounds.size.height);
_progressLayer.colors = #[(id)[[UIColor colorWithRed:0/255.0 green:0/250.0 blue:250.0/255.0 alpha:1.0f] CGColor],
(id)[[UIColor colorWithRed:150.0/200.0 green:150.0/200.0 blue:150.0/200.0 alpha:.5] CGColor],
(id)[[UIColor colorWithRed:200.0/200.0 green:200.0/200.0 blue:200.0/200.0 alpha:.5] CGColor],
(id)[[UIColor colorWithWhite:0.3f alpha:0.1f] CGColor]];
_progressLayer.locations = #[#0.00f, #0.2f, #0.90f, #1.00f];
[self.layer insertSublayer:_progressLayer atIndex:1];
//}
}
I have no idea what is happening, and I don't seem to be able to access the reused cell in order to redraw it.
These are the deletion methods:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
[self deactivateTimers];
NSArray * days = [NSArray arrayWithObjects: #"anyday", #"monday", #"tuesday", #"wednesday", #"thursday", #"friday", #"saturday", #"sunday", nil];
Task * task = self.tasksByDay[days[indexPath.section]][indexPath.row];
if ([task.weekly boolValue]){
task.finished = [NSNumber numberWithBool:1];
} else {
[managedObjectContext deleteObject:task];
}
[self.managedObjectContext save:nil];
[self grabTasksFromContext];
}
}
-(void) grabTasksFromContext{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Task"
inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSError *error;
NSMutableArray * managedObjects = [NSMutableArray arrayWithArray:[managedObjectContext
executeFetchRequest:fetchRequest error:&error]];
int numObjects = [managedObjects count];
for (int i = 0; i < numObjects; i ++){
Task * task = [managedObjects objectAtIndex:i];
NSLog(#"name %#", task.name);
// if the task is finished we don't want it to be displayed in the list
if ([task.finished boolValue]){
NSLog(#"finished");
[managedObjects removeObject:task];
i -= 1;
numObjects -= 1;
}
}
self.tasks = managedObjects;
// This implementation is pretty ugly
// I'm sorry about that and will fix it in the future
// probably the more attractive way to do this is to make an array of the days, and then cycle through that and check through the array of tasks
monday = tuesday = wednesday = thursday = friday = saturday = sunday = anyday = 0;
NSMutableArray * mondayArray = [[NSMutableArray alloc] init];
NSMutableArray * tuesdayArray = [[NSMutableArray alloc] init];
NSMutableArray * wednesdayArray = [[NSMutableArray alloc] init];
NSMutableArray * thursdayArray = [[NSMutableArray alloc] init];
NSMutableArray * fridayArray = [[NSMutableArray alloc] init];
NSMutableArray * saturdayArray = [[NSMutableArray alloc] init];
NSMutableArray * sundayArray = [[NSMutableArray alloc] init];
NSMutableArray * anydayArray = [[NSMutableArray alloc] init];
for (Task * task in self.tasks){
if ([task.day isEqualToString:#"monday"]){
mondayArray[monday] = task;
monday++;
} else if ([task.day isEqualToString:#"tuesday"]){
tuesdayArray[tuesday] = task;
tuesday++;
} else if ([task.day isEqualToString:#"wednesday"]){
wednesdayArray[wednesday] = task;
wednesday++;
} else if ([task.day isEqualToString:#"thursday"]){
thursdayArray[thursday] = task;
thursday++;
} else if ([task.day isEqualToString:#"friday"]){
fridayArray[friday] = task;
friday++;
} else if ([task.day isEqualToString:#"saturday"]){
saturdayArray[saturday] = task;
saturday++;
} else if ([task.day isEqualToString:#"sunday"]){
sundayArray[sunday] = task;
sunday++;
} else {
anydayArray[anyday] = task;
anyday++;
}
}
self.tasksByDay = [[NSDictionary alloc] initWithObjectsAndKeys: mondayArray,#"monday",
tuesdayArray, #"tuesday", wednesdayArray, #"wednesday", thursdayArray, #"thursday",
fridayArray, #"friday", saturdayArray, #"saturday", sundayArray, #"sunday",
anydayArray, #"anyday", nil];
[self.tableView reloadData];
}
Any help or thoughts on what is happening would be appreciated.
However, when I delete a cell, that drawing drops to the cell below it.
This is the key observation: it indicates to me that at the time the redraw is happening, the model (i.e. your self.tasksByDay[dayName]) has not been updated yet. When a cell at a certain row is deleted, the taskByDay for the corresponding day needs to be updated to remove the corresponding row from the NSArray. If this does not happen, the data for the deleted task would influence the drawing of a cell at the next index, thus the visuals wold appear to "drop" by one row. From your description it sounds like this is precisely what is happening.
You need to make sure that by the time the table is refreshed (or the notification of a cell deletion is sent to the UITableView) the model has been updated already to not have the row being deleted. This way the table visuals would update as you expect them to.
Not directly related to the problem, but if you create an array
NSArray *dayName = #[#"anyday", #"monday", #"tuesday", #"wednesday", etc.];
you can replace the long chain of ifs with
task = [self.tasksByDay[dayName objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];
The other problem in your code is that every time that you call drawFillInAtPercent:, a new CAGradientLayer is added. As you scroll up and down, reused cells accumulate new layers without ever getting rid of the previously added ones. You need to change your code to add the gradient layer only once, and then reusing the existing one inside your drawFillInAtPercent: method. For example, you can add the layer in the designated initializer of the AGProgressViewCell, assigning it to _progressLayer instance variable, and adding it to the layer hierarchy once. From then on, drawFillInAtPercent: would change the existing _progressLayer, rather than creating new ones each time.
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];