UILabel in a custom UITableView cell not updating when master data changes

I am trying to create a scoring application that uses a custom table cell with photos of players, names, buttons, etc ... There are add / subtract buttons directly in the user table cell that hit my save method, and it saves it back to Core Data for that particular user.

The problem is that the on-screen rating is not updated and does not reflect the change. After the save action in Core Data is completed, I call [self.tableView reloadData]; ... nothing. However, if I restart the application, a change in points will appear (for any of the players I clicked on).

Maybe I'm making it harder than it should be, or this, or I just don't understand the real problem.

Thoughts / comments? Thanks in advance. :-)

Sorry if this is too much, but here is the main part 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]; } 

enter image description here


UPDATE: Thanks for the bbarnhart hint ... I read this post earlier and used this for the foundation I started with. I decided to take another step and reorganize the code snippet using more of the example of Ray Wenderlich.

I saw some improvements in what is being recorded and reported back through NSLog ... but the view still does not change.

The action increments the score, and then I reset the cell using [self configureCell:cell atIndexPath:path]; There ... a method that is responsible for sending text to the display ... NSLog shows 2014-12-04 22:40:40.199 appName[7153:150248] Score for Tim: 4 , while the display so far shows only 3.

I know this is some kind of stupid rookie move ... I'm just doing something dead that I can't understand. Here is a snippet of modified 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: Some time ago, when I had a chance to return, and just noticed a new problem, since you have tips. When scrolling down or up in a list and moving beyond normal boundaries, the lookup table data seems to overwrite the display for a line above or below the current line. Strange ... Not sure if this animated Gif will appear on Stack. Here is an example:

enter image description here

+1
source share
1 answer

The main reason the table dynamic view is not updated is that NSFetchedResultsController uses a delegate to notify you when changes occur. You will need to set this delegate, self.fetchedResultsController.delegate = self , and then add the delegate methods.

Here is a link to an example for managing a UITableView with NSFetchedResultsController .

Update

Deploy these NSFetchResultsController delegate methods to dynamically update your table.

 - (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 

Typically, these methods contain template code for updating your table, which you will also find in the link above.

+1
source

Source: https://habr.com/ru/post/902111/


All Articles