IOS Core Data, UITableView load more data - ios

in my IOS project i using Core Data. I have 1000 elements
First of all, I need to show in UITableView 30 elements. When the user scrolls and reaches the bottom of an UITableView (5 elements to the end) so I can load new data into the table.
How can i do that
I used this code but it does not work the way I wanted
#import "HomeViewController.h"
#import "GDMnLineRKObjectManager.h"
#import "CoreData+MagicalRecord.h"
#import "CinNames.h"
#import "Event.h"
#interface HomeViewController ()<UITableViewDataSource, UITableViewDelegate, NSFetchedResultsControllerDelegate>
#property (weak, nonatomic) IBOutlet UITableView *tableView;
#property NSUInteger dpc;
#property BOOL is_load;
#property BOOL process_loading;
#property NSFetchRequest *fetchRequest;
#property (nonatomic) BOOL loadingMoreTableViewData;
#property (nonatomic) NSUInteger inf_counter;
#end
#implementation HomeViewController
//#synthesize tableView;
- (IBAction)showMenu
{
// Dismiss keyboard (optional)
//
[self.view endEditing:YES];
[self.frostedViewController.view endEditing:YES];
// Present the view controller
//
[self.frostedViewController presentMenuViewController];
[self.tableView reloadData];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)saveToStore
{
// Saving to persistent store for further usage.
}
- (void)viewDidLoad {
[super viewDidLoad];
self.dpc = 0;
self.inf_counter = self.dpc;
self.is_load = NO;
self.process_loading = NO;
self.fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass([Event class])];
// Do any additional setup after loading the view, typically from a nib.
/*
*/
[self loadElements];
////
[self.tableView setDelegate:self];
[self.tableView setDataSource:self];
///
}
- (void)loadElements
{
// Get an array of remote "character" objects. Specify the offset.
[[GDMnLineRKObjectManager manager] getMnLineObjectsAtPath:SERVER_PATH_LOAD
parameters:#{#"someval" : #("564")}
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
}
failure:^(RKObjectRequestOperation *operation, NSError *error) {
// Failed to load characters.
/*
[self animateActivityIndicator:NO];
[bottomPullView finishedLoading];
*/
[[[UIAlertView alloc] initWithTitle:#"Marvel API Error" message:operation.error.localizedDescription delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:#"Retry", nil] show];
}];
}
#pragma mark - Table View
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [[self.fetchedResultsController sections] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
id <NSFetchedResultsSectionInfo> sectionInfo = [self.fetchedResultsController sections][section];
return [sectionInfo numberOfObjects];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"III = %#", indexPath);
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"HuCell" forIndexPath:indexPath];
[self configureCell:cell atIndexPath:indexPath];
if (indexPath.row > self.inf_counter - 5) {
// User has scrolled to the bottom of the list of available data so simulate loading some more if we aren't already
if (!self.loadingMoreTableViewData && self.process_loading == NO) {
self.loadingMoreTableViewData = YES;
self.process_loading = YES;
[self performSelector:#selector(addSomeMoreEntriesToTableView) withObject:nil afterDelay:0.0f];
}
}
if (indexPath.row < self.inf_counter) {
[self configureCell:cell atIndexPath:indexPath];
} else {
cell.textLabel.text = #"Loading more data...";
}
return cell;
}
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
NSManagedObject *object = [self.fetchedResultsController objectAtIndexPath:indexPath];
NSString * string3 = [NSString stringWithFormat:#"%# - %ld", [[object valueForKey:#"name"] description], (long)indexPath.row];
cell.textLabel.text = string3;//[[object valueForKey:#"name"] description];
}
#pragma mark - Fetched results controller
- (NSFetchedResultsController *)fetchedResultsController
{
NSLog(#"Skolko");
if ((!_fetchedResultsController || self.process_loading == YES)) {
self.process_loading = NO;
self.is_load = NO;
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"timeStamp" ascending:NO];
self.fetchRequest.sortDescriptors = #[sortDescriptor];
self.fetchRequest.fetchLimit = self.dpc;
//self.fetchRequest.fetchBatchSize = 30;
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:self.fetchRequest managedObjectContext:[RKManagedObjectStore defaultStore].mainQueueManagedObjectContext sectionNameKeyPath:nil cacheName:nil];
self.fetchedResultsController.delegate = self;
NSError *error;
[self.fetchedResultsController performFetch:&error];
NSLog(#"%#",[self.fetchedResultsController fetchedObjects]);
NSLog(#"Counta: %lu",(unsigned long)[self.fetchedResultsController.fetchedObjects count]);
NSAssert(!error, #"Error performing fetch request: %#", error);
self.inf_counter = [self.fetchedResultsController.fetchedObjects count];
[self.tableView reloadData];
}
return _fetchedResultsController;
}
- (void)addSomeMoreEntriesToTableView {
self.dpc += 20;
[self fetchedResultsController];
self.loadingMoreTableViewData = NO;
[self.tableView reloadData];
}
- (void)scrollViewDidScroll: (UIScrollView*)scroll {
// UITableView only moves in one direction, y axis
CGFloat currentOffset = scroll.contentOffset.y;
CGFloat maximumOffset = scroll.contentSize.height - scroll.frame.size.height;
NSLog(#"hui = %f", (maximumOffset - currentOffset));
// Change 10.0 to adjust the distance from bottom
if (maximumOffset - currentOffset <= 10.0) {
self.is_load = YES;
self.dpc += 10;
[self fetchedResultsController];
//[self.tableView reloadData];
}
}
#end

What you are looking for is the fetchBatchSize property on NSFetchRequest. I hope you are also using an NSFetchedResultsController, it goes fantastically well with Table Views and Collection Views. Here's the description:
You use a fetched results controller to efficiently manage the results
returned from a Core Data fetch request to provide data for a
UITableView object.
So before you setup your fetched results controller just ensure you set the fetchBatchSize of the fetch request and let the framework handle all the optimizations for you.
EDIT
The OP wanted the original fetch to only include 30 items and then only if the user scrolled it should repopulate the fetch. In most cases fetchBatchSize should be the solution as Core Data will seek to fault all the results and thus avoid the overhead of fetching all the objects, preferring to lazily fault them instead. To stick with the OP, the solution might be like this:
- (NSFetchRequest *)createFetchRequest {
NSFetchRequest *fetch = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass([MyEntity class])];
fetch.predicate = //set up your fetch
return fetch;
}
And then when you instantiate your controller you can set the fetch limit:
NSFetchRequest *fetchRequest = [self createFetchRequest];
fetchRequest.fetchLimit = 30;
NSFetchedResultsController *controller = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:context
sectionNameKeyPath:nil cacheName:nil];
self.controller = controller;
since the fetchRequest property on the controller is readOnly you are forced to re-assign your controller when you want to fetch everything:
NSFetchRequest *fetchRequest = [self createFetchRequest];
NSFetchedResultsController *controller = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:context
sectionNameKeyPath:nil cacheName:nil];
[controller performFetch:nil];
self.controller = controller;
If you want even more to go on, head on over to Ray Wenderlich, they provide awesome tutorials for everything iOS.
Happy Coding

Options:
Watch the session Advanced ScrollView Techniques from WWDC 2011.
Tutorial: http://mobiledevelopertips.com/user-interface/creating-circular-and-infinite-uiscrollviews.html

Related

Sorting using NSSortDescriptor by time bug (with core data)

please try to help me here I have some really weird bug(s) trying to sort my string table view objects.
The behaviour is simple, I have:
CreateViewController - here I create strings and add them to core data
StackTableViewController - this is the table view for the strings I create. They should be sorted by date, the first cell is the oldest, the second cell created after him and so on...
HomeViewController - here I have a UILabel that holds the first object of the table view all the time, if I delete it in the table I update this label with a delegate method,
So this is it.
The problem:
If there is NO strings in the table view and then I add a string in the create page, it gets created fine, but if add another one , the new one is at top of the list...but if I terminate the app and open it again the order is fine.
there are also some other weird behaviour after I add more strings...But the thing is, whenever I terminate the app and open it again everything is ordered perfectly..:/
This is the relevant code:
CreateViewController.m
- (id)init {
self = [super initWithNibName:#"CreateViewController" bundle:nil];
if (self) {
}
return self;
}
- (void)save {
if (self.myTextView.text.length == 0) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Hey!" message:#"You can't save a blank string" delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil];
[alert show];
}
[self insertTeget];
[self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
[self.myTextView resignFirstResponder];
}
//this is where I add the string to the NSManagedObject object I have called 'Target'
- (void)insertTeget {
CoreDataStack *stack = [CoreDataStack defaultStack];
Target *target = [NSEntityDescription insertNewObjectForEntityForName:#"Target" inManagedObjectContext:stack.managedObjectContext];
if (self.myTextView.text != nil) {
target.body = self.myTextView.text;
}
[stack saveContext];
}
This is the table view:
StackTableViewController.m
- (id)init {
self = [super initWithNibName:#"StackTableViewController" bundle:nil];
if (self) {
[self fetchData];
}
return self;
}
//currentTarget is a string I have in the .h file to pass the home view controller the first string of the table view
- (void)fetchData {
[self.fetchedResultController performFetch:nil];
if (self.fetchedResultController.fetchedObjects.count > 0) {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:0];
Target *current = [self.fetchedResultController objectAtIndexPath:indexPath];
self.currentTarget = current.body;
}else {
self.currentTarget = nil;
}
}
- (void)viewDidLoad {
[super viewDidLoad];
[self fetchData];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return self.fetchedResultController.sections.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
id <NSFetchedResultsSectionInfo> sectionInfo = [self.fetchedResultController sections][section];
return [sectionInfo numberOfObjects];
}
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
return UITableViewCellEditingStyleDelete;
}
//here I'm updating the delegate when A cell was deleted so I can update the label in the home view controller
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
Target *target = [self.fetchedResultController objectAtIndexPath:indexPath];
CoreDataStack *stack = [CoreDataStack defaultStack];
[[stack managedObjectContext] deleteObject:target] ;
[stack saveContext];
if ([_delegate respondsToSelector:#selector(didDeleteObject)]) {
[self fetchData];
[_delegate didDeleteObject];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"StackTableViewCell";
Target *target = [self.fetchedResultController objectAtIndexPath:indexPath];
StackTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell)
{
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"StackTableViewCell" owner:self options:nil];
cell = [topLevelObjects objectAtIndex:0];
}
cell.cellLabel.text = target.body;
return cell;
}
// this is my fetch request where im setting up the sort descriptor
- (NSFetchRequest *)targetsFetchRequest {
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Target"];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"time" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
return fetchRequest;
}
- (NSFetchedResultsController *)fetchedResultController {
if (_fetchedResultController != nil) {
return _fetchedResultController;
}
CoreDataStack *stack = [CoreDataStack defaultStack];
NSFetchRequest *fetchRequest = [self targetsFetchRequest];
_fetchedResultController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:stack.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
_fetchedResultController.delegate = self;
return _fetchedResultController;
}
This is the home view controller where Im updating the label:
- (id)init {
self = [super initWithNibName:#"HomeViewController" bundle:nil];
if (self) {
stackTableViewController = [[StackTableViewController alloc] init];
stackTableViewController.delegate = self;
}
return self;
}
- (void)viewWillAppear:(BOOL)animated {
[stackTableViewController fetchData];
self.homeLabel.text = stackTableViewController.currentTarget;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.homeLabel.text = stackTableViewController.currentTarget;
}
//this is the delegate method
- (void)didDeleteObject {
self.homeLabel.text = stackTableViewController.currentTarget;
}
- (IBAction)goToStack:(id)sender {
[self.navigationController pushViewController:stackTableViewController animated:YES];
}
- (IBAction)goToCreate:(id)sender {
CreateViewController *createViewController = [[CreateViewController alloc]initWithNibName:#"CreateViewController" bundle:nil];
UINavigationController *navigationController = [[UINavigationController alloc]initWithRootViewController:createViewController];
[self.navigationController presentViewController:navigationController animated:YES completion:nil]}
Please please please, it's driving me crazy, why the order is fine only after i terminate the app?
thanks###!
I don't see you setting time anywhere. It should be set to [NSDate date] when you create the object, if it's acting as a creation date.

UILabel in custom UITableView cell not updating with Core Data change

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.

Populating Core Data into Table View

I am trying to display data in a custom tableview. Idea is that in ViewController A i populate data into the Core Data entity, in ViewController B I retrieve data from Core Data entity successfully and display it in table view with no problem. Here comes the problem. If I delete data from entity and tableview in ViewController B, go back to ViewController A and add new item to Core Data again, it does not appear in tableview when I go back to ViewController B. But if i close and open the application again, the item appears there again.
So the problem is that the item appear in core data after deleting an item and inserting again, but doesnt appear in tableview. Maybe i have to update tableview somehow, but i tried [self.tableview reloadData]; and it did not help. Code Below. Any Ideas???
ViewController A
#import "ListItem.h"
#import "ViewOrders.h"
#import "MainView.h"
#import "Order_list.h"
#import "AppDelegate.h"
#interface ListItem ()
#property (nonatomic, strong) NSManagedObjectContext *managedObjectContext;
#property (nonatomic, retain) NSFetchedResultsController *results;
#property (nonatomic, strong) NSMutableArray *namesArray;
#property (nonatomic, strong) NSMutableArray *quantityArray;
#property (nonatomic, strong) NSMutableArray *priceArray;
#property (nonatomic, strong) NSMutableArray *imagesArray;
#end
#implementation ListItem
#synthesize managedObjectContext = _managedObjectContext;
#synthesize results = _results;
#synthesize imageView = _imageView;
#synthesize mainView = _mainView;
#synthesize nameField = _nameField;
#synthesize descField = _descField;
#synthesize priceField = _priceField;
#synthesize quantityField = _quantityField;
#synthesize quantityStepperOutlet = _quantityStepperOutlet;
#synthesize namesArray =_namesArray;
#synthesize quantityArray = _quantityArray;
#synthesize priceArray = _priceArray;
#synthesize imagesArray = _imagesArray;
- (void)viewDidLoad
{
[super viewDidLoad];
AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
self.managedObjectContext = delegate.managedObjectContext;
//self.navigationItem.hidesBackButton = YES;
self.namesArray = [[NSMutableArray alloc] init];
self.quantityArray = [[NSMutableArray alloc] init];
self.priceArray = [[NSMutableArray alloc] init];
self.imagesArray = [[NSMutableArray alloc] init];
UIGraphicsBeginImageContext(self.view.frame.size);
[[UIImage imageNamed:#"bella_italia_crop.png"] drawInRect:self.view.bounds];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.view.backgroundColor = [UIColor colorWithPatternImage:image];
UIGraphicsBeginImageContext(self.mainView.frame.size);
[[UIImage imageNamed:#"bella_italia_crop.png"] drawInRect:self.mainView.bounds];
UIImage *image2 = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.mainView.backgroundColor = [UIColor colorWithPatternImage:image2];
self.imageView.image = [UIImage imageNamed:self.thumbnail];
self.imageView.contentMode = UIViewContentModeScaleAspectFit;
self.nameField.text = self.name;
self.descField.text = self.description;
self.priceField.text = self.price;
self.descField.backgroundColor = [UIColor clearColor];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"viewAllOrdersSegue"])
{
ViewOrders *vo = segue.destinationViewController;
vo.namesArray = self.namesArray;
vo.quantityArray = self.quantityArray;
vo.priceArray = self.priceArray;
vo.tableNumber = #"1";
vo.imagesArray = self.imagesArray;
}
else if ([segue.identifier isEqualToString:#"unwindToMain"])
{
MainView *mv = segue.destinationViewController;
mv.namesArray = self.namesArray;
mv.quantityArray = self.quantityArray;
mv.priceArray = self.priceArray;
mv.imagesArray = self.imagesArray;
}
}
- (IBAction)quantityStepper:(UIStepper *)sender
{
int stepper = [sender value];
self.quantityField.text = [NSString stringWithFormat:#"%d", stepper];
}
- (IBAction)addOrderAction:(UIButton *)sender
{
NSLog(#"Add order action button pressed");
Order_list *newItem = [NSEntityDescription insertNewObjectForEntityForName:#"Order_list" inManagedObjectContext:self.managedObjectContext];
newItem.title = self.name;
newItem.quantity = #([self.quantityField.text floatValue]);
newItem.price = #([self.priceField.text floatValue]);
newItem.tableNumber = [NSNumber numberWithInt:1];
newItem.thumbnail = self.thumbnail;
newItem.id = [NSNumber numberWithInt:0];
NSError *error;
[self.managedObjectContext save:&error];
UIAlertView *successAlert = [[UIAlertView alloc] initWithTitle:#"Success!" message:#"Your order was successfully added to orders list! Press View Orders to finalize the order or go back to menu and choose something else." delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil];
successAlert.alertViewStyle = UIAlertViewStyleDefault;
[successAlert show];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Order_list" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSArray *fetchedObjects = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
for (NSManagedObjectContext *info in fetchedObjects) {
NSLog(#"Title: %# %#", [info valueForKey:#"title"], [info valueForKey:#"quantity"]);
}
//[self.namesArray addObject:self.name];
//[self.quantityArray addObject:self.quantityField.text];
//int finalPrice = [self.quantityField.text intValue] * [self.price intValue];
//[self.priceArray addObject:[NSString stringWithFormat:#"%d", finalPrice]];
//[self.imagesArray addObject:self.thumbnail];
//NSLog(#"Names: %#, Quantities: %#, Prices: %#", self.namesArray, self.quantityArray, self.priceArray);
}
#end
ViewController B
#import "ViewOrders.h"
#import "AppDelegate.h"
#import "Order_list.h"
#import "ViewOrdersCell.h"
#interface ViewOrders ()
#property (nonatomic, strong) NSMutableData *requestData;
#property (nonatomic, strong) NSManagedObjectContext *managedObjectContext;
#property (nonatomic, retain) NSFetchedResultsController *results;
#property (nonatomic, strong) NSString* orderState;
#property (nonatomic, strong) UIAlertView *billAlert;
#end
#implementation ViewOrders
#synthesize managedObjectContext = _managedObjectContext;
#synthesize results = _results;
#synthesize confirmOrderButton = _confirmOrderButton;
#synthesize requestData = _requestData;
#synthesize orderState = _orderState;
#synthesize billAlert = _billAlert;
- (void)viewDidLoad
{
[super viewDidLoad];
self.billAlert = [[UIAlertView alloc] initWithTitle:#"Order Confirmed!" message:#"Your order list has been successfully confirmed and is sent to kitchen! Press Request Bill Now or Request Bill Later to Continue (You cannot make any new orders until you request a bill for current one! To trigger this screen again, press and hold on any order in pending orders page. Thanks!)" delegate:self cancelButtonTitle:#"Request Bill Later" otherButtonTitles:#"Request Bill Now", nil];
self.billAlert.tag = 2;
self.billAlert.alertViewStyle = UIAlertViewStyleDefault;
AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
self.managedObjectContext = delegate.managedObjectContext;
self.namesArray = [[NSMutableArray alloc] init];
self.quantityArray = [[NSMutableArray alloc] init];
self.imagesArray = [[NSMutableArray alloc] init];
self.priceArray = [[NSMutableArray alloc] init];
NSFetchRequest *coreRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Order_list" inManagedObjectContext:self.managedObjectContext];
[coreRequest setEntity:entity];
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:#"title" ascending:YES];
[coreRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
[coreRequest setFetchBatchSize:20];
NSFetchedResultsController *controller = [[NSFetchedResultsController alloc] initWithFetchRequest:coreRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:#"Root"];
self.results = controller;
self.results.delegate = self;
NSError *error;
[self.results performFetch:&error];
NSArray *fetchedObjects = [self.managedObjectContext executeFetchRequest:coreRequest error:&error];
for (NSManagedObjectContext *info in fetchedObjects)
{
self.orderState = [NSString stringWithFormat:#"%#", [info valueForKey:#"id"]];
[self.namesArray addObject:[info valueForKey:#"title"]];
[self.quantityArray addObject:[info valueForKey:#"quantity"]];
[self.imagesArray addObject:[info valueForKey:#"thumbnail"]];
[self.priceArray addObject:[info valueForKey:#"price"]];
//NSLog(#"List: %#", [info valueForKey:#"title"]);
}
if ([self.orderState isEqualToString:#"1"])
{
self.confirmOrderButton.enabled = false;
}
//self.navigationController.navigationItem.backBarButtonItem.enabled = false;
//self.navigationItem.hidesBackButton = YES;
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
id sectionInfo = [[self.results sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"ordersCell" forIndexPath:indexPath];
ViewOrdersCell *cell = [tableView dequeueReusableCellWithIdentifier:#"ordersCell" forIndexPath:indexPath];
cell.title.text = [self.namesArray objectAtIndex:indexPath.row];
cell.thumbnail.image = [UIImage imageNamed:[self.imagesArray objectAtIndex:indexPath.row]];
cell.status.text = #"Waiting to Confirm";
if ([self.orderState isEqualToString:#"0"])
{
cell.status.text = #"Waiting to Confirm";
cell.status.textColor = [UIColor redColor];
}
else
{
cell.status.textColor = [UIColor greenColor];
cell.status.text = #"Confirmed";
}
UILongPressGestureRecognizer *longPressRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(onLongPress:)];
[cell addGestureRecognizer:longPressRecognizer];
//cell.textLabel.text = [self.namesArray objectAtIndex:indexPath.row];
//cell.imageView.image = [UIImage imageNamed:[self.imagesArray objectAtIndex:indexPath.row]];
//cell.backgroundColor = [UIColor clearColor];
return cell;
}
// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
return YES;
}
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete)
{
// Delete the row from the data source
NSManagedObject *managedObject = [self.results objectAtIndexPath:indexPath];
[self.managedObjectContext deleteObject:managedObject];
[self.managedObjectContext save:nil];
//[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
//[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
[self.tableView beginUpdates];
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
[self.tableView endUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
if (type == NSFetchedResultsChangeInsert)
{
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
}
if (type == NSFetchedResultsChangeDelete)
{
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
if (type == NSFetchedResultsChangeUpdate)
{
}
if (type == NSFetchedResultsChangeMove)
{
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id<NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
{
if (type == NSFetchedResultsChangeInsert)
{
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
}
if (type == NSFetchedResultsChangeDelete)
{
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
}
}
/*
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
}
*/
/*
// Override to support conditional rearranging of the table view.
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the item to be re-orderable.
return YES;
}
*/
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if(buttonIndex == 1 && alertView.tag == 1)
{
NSLog(#"Confirm Pressed");
//////Send Order to the Server//////
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Order_list"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"id=0"];
fetchRequest.predicate = predicate;
NSError *error = nil;
NSArray *array = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
for (NSManagedObjectContext *info in array)
{
NSNumber *temp = [[NSNumber alloc] initWithInt:1];
[info setValue:temp forKey:#"id"];
}
[self.managedObjectContext save:&error];
[self.tableView reloadData];
self.confirmOrderButton.enabled = false;
[self.billAlert show];
}
else if (buttonIndex == 1 && alertView.tag == 2)
{
self.confirmOrderButton.enabled = true;
UIAlertView *billRequest = [[UIAlertView alloc] initWithTitle:#"Bill Request Successful!" message:#"Your bill has been requested successfully! Waiter will deliver it to you as soon as possible! Thanks for visiting Bella Italia!" delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil];
billRequest.alertViewStyle = UIAlertViewStyleDefault;
billRequest.tag = 3;
[billRequest show];
NSFetchRequest *fetchItems = [[NSFetchRequest alloc] init];
[fetchItems setEntity:[NSEntityDescription entityForName:#"Order_list" inManagedObjectContext:self.managedObjectContext]];
[fetchItems setIncludesPropertyValues:NO]; //only fetch the managedObjectID
NSError *error = nil;
NSArray *array = [self.managedObjectContext executeFetchRequest:fetchItems error:&error];
[self.results performFetch:&error];
//error handling goes here
for (NSManagedObject *item in array)
{
[self.managedObjectContext deleteObject:item];
}
NSError *saveError = nil;
[self.managedObjectContext save:&saveError];
}
else if (buttonIndex == 0 && alertView.tag == 2)
{
}
else if (buttonIndex == 0 && alertView.tag == 3)
{
[self.navigationController popToRootViewControllerAnimated:UITableViewRowAnimationFade];
}
}
- (IBAction)confirmOrder:(UIBarButtonItem *)sender
{
NSLog(#"Confirm Order");
UIAlertView *confOrder = [[UIAlertView alloc] initWithTitle:#"Confirm Order!" message:#"This will send your order to process. Once this is done, no more changes are available. Are you sure you want to confirm your order?" delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:#"Confirm", nil];
confOrder.alertViewStyle = UIAlertViewStyleDefault;
confOrder.tag = 1;
[confOrder show];
}
-(void)onLongPress:(UILongPressGestureRecognizer*)pGesture
{
NSLog(#"Long Press");
[self.billAlert show];
}
#end
EDIT
Forgot to mention that i am using Custom Cell. Maybe that affects the situation somehow?
I tried to NSLog the variables which store the data that supposed to appear in tableview. All the data is beeing passed, but it does not appear in tableview. Logically [self.tableView reloadData in viewWillAppear should do the trick, but it does not........
I have a similar setup in one my apps and I add an observer to the ViewController with the table that wants updating;
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(reload) name:#"UpdatedCoreData" object:nil];
I clear any arrays or dictionaries I use in the reload method and call (inside my async queue);
[self.tableView performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:NO];
Then when I finish updating the core data in my other VC I call it;
[[NSNotificationCenter defaultCenter] postNotificationName:#"UpdatedCoreData" object: nil];
And that works perfectly fine for me, I don't know if that will help you at all.
EDIT:
Along the lines of my reload method;
- (void)reload {
// Do any potential clean up if neccesary
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
// Fetch core data & populate dictionaries/arrays
[self.tableView performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:NO];
});
}

Adding Search Display Controller in UIViewController

I want to implement a UISearchDisplayController in a UIViewController, hence there is no UITableView implemented. I created the UISearchBar and UISearchDisplayController and implemented the corresponding delegate methods.
When I run the app and try searching, the table view that should be shown with the search results do not appear. To explain more my app UI, the UIViewController has a map and the search bar is placed in the navigation controller.
What I understood for now, it seems like I should implement a table view in order to be reused for the search results. However, there is no place/need to place a table view. What can I do to fix this? Any hints?
Here is the code how I implemented everything:
- (void)viewDidLoad{
[super viewDidLoad];
// Do any additional setup after loading the view.
_search = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 100, 30)];
[_search setAutoresizingMask:UIViewAutoresizingFlexibleWidth];
[_search setPlaceholder:#"Search"];
self.navigationItem.titleView = _search;
_searchArray = [[NSMutableArray alloc] initWithObjects:#"Hi", nil];
_searchDisplay = [[UISearchDisplayController alloc] initWithSearchBar:_search contentsController:self];
_searchDisplay.delegate = self;
_searchDisplay.searchResultsDataSource = self;
_searchDisplay.searchResultsDelegate = self;
[_searchDisplay setActive:YES animated:YES];
filteredResults = [[NSMutableArray alloc] init];}
-(void) filterForSearchText:(NSString *) text scope:(NSString *) scope
{
[filteredResults removeAllObjects]; // clearing filter array
NSPredicate *filterPredicate = [NSPredicate predicateWithFormat:#"SELF contains[c] %#",text]; // Creating filter condition
filteredResults = [NSMutableArray arrayWithArray:[_searchArray filteredArrayUsingPredicate:filterPredicate]]; // filtering result
NSLog(#"search %#", filteredResults);
}
#pragma mark - UISearchDisplayDelegate Methods
-(BOOL) searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterForSearchText:searchString scope:[[[[self searchDisplayController] searchBar] scopeButtonTitles] objectAtIndex:[[[self searchDisplayController] searchBar] selectedScopeButtonIndex] ]];
return YES;
}
-(BOOL) searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption
{
[self filterForSearchText:self.searchDisplayController.searchBar.text scope:
[[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:searchOption]];
return YES;
}
#import <UIKit/UIKit.h>
#import "RoleDetailTVC.h" // so this class can be an RoleDetailTVCDelegate
#import "CoreDataTableViewController.h" // so we can fetch
#import "Role.h"
#interface RolesTVC : CoreDataTableViewController <RoleDetailTVCDelegate>
#property (strong, nonatomic) NSFetchedResultsController *fetchedResultsController;
#property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;
#property (strong, nonatomic) Role *selectedRole;
#property (nonatomic, retain) NSMutableArray *searchResults;
#end
#import "RolesTVC.h"
#import "Role.h"
#import "ModelCell.h"
#import <QuartzCore/QuartzCore.h>
#interface RolesTVC ()
{
NSMutableArray *objects;
}
#end
#implementation RolesTVC
#synthesize fetchedResultsController = __fetchedResultsController;
#synthesize managedObjectContext = __managedObjectContext;
#synthesize selectedRole;
#synthesize searchResults;
-(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
objects = [[NSMutableArray alloc]init];
if(self){
for (int i=1; i<100; i++) {
NSString *str = [NSString stringWithFormat:#"This is the fabulous Row %d",i];
[objects addObject:str];
}
}
return self;
}
//This function is where all the magic happens
-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath{
//1. Setup the CATransform3D structure
CATransform3D rotation;
rotation = CATransform3DMakeRotation( (90.0*M_PI)/180, 0.0, 0.7, 0.4);
rotation.m34 = 1.0/ -600;
//2. Define the initial state (Before the animation)
cell.layer.shadowColor = [[UIColor blackColor]CGColor];
cell.layer.shadowOffset = CGSizeMake(10, 10);
cell.alpha = 0;
cell.layer.transform = rotation;
cell.layer.anchorPoint = CGPointMake(0, 0.5);
//!!!FIX for issue #1 Cell position wrong------------
if(cell.layer.position.x != 0){
cell.layer.position = CGPointMake(0, cell.layer.position.y);
}
//4. Define the final state (After the animation) and commit the animation
[UIView beginAnimations:#"rotation" context:NULL];
[UIView setAnimationDuration:0.8];
cell.layer.transform = CATransform3DIdentity;
cell.alpha = 1;
cell.layer.shadowOffset = CGSizeMake(0, 0);
[UIView commitAnimations];
}
//Helper function to get a random float
- (float)randomFloatBetween:(float)smallNumber and:(float)bigNumber {
float diff = bigNumber - smallNumber;
return (((float) (arc4random() % ((unsigned)RAND_MAX + 1)) / RAND_MAX) * diff) + smallNumber;
}
- (UIColor*)colorFromIndex:(int)index{
UIColor *color;
//Purple
if(index % 3 == 0){
color = [UIColor colorWithRed:0.93 green:0.01 blue:0.55 alpha:1.0];
//Blue
}else if(index % 3 == 1){
color = [UIColor colorWithRed:0.00 green:0.68 blue:0.94 alpha:1.0];
//Blk
}else if(index % 3 == 2){
color = [UIColor blackColor];
}
else if(index % 3 == 3){
color = [UIColor colorWithRed:0.00 green:1.0 blue:0.00 alpha:1.0];
}
return color;
}
#pragma mark -
#pragma mark Fetched Results Controller section
- (void)setupFetchedResultsController
{
// 1 - Decide what Entity you want
NSString *entityName = #"Role"; // 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];
[request setFetchLimit:100];
// 3 - Filter it if you want
//request.predicate = [NSPredicate predicateWithFormat:#"Role.name = Blah"];
// 4 - Sort it if you want
request.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"name"
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];
}
/**
* iCloud
*
* #param note Check PersonTVC
*/
- (void)reloadFetchedResults:(NSNotification*)note {
//NSLog(#"Underlying data changed ... refreshing!");
[self performFetch];
}
- (void)viewDidLoad
{
[ADVThemeManager customizeTableView:self.tableView];
[self.tableView reloadData];
/*
* Refresh this view whenever data changes iCloud
*/
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(reloadFetchedResults:)
name:#"RolesTVCSomethingChanged"
object:[[UIApplication sharedApplication] delegate]];
}
- (void)viewDidUnload
{
/**
* iCloud
*
*/
[[NSNotificationCenter defaultCenter] removeObserver:self];
/**
* Search Function
*/
self.searchResults = nil;
}
#pragma mark -
#pragma mark - Table view delegate
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"RolesCell";
ModelCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"ModelCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
// Configure the cell...
Role *role = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.nameLbl.text = role.name;
cell.zoneLbl.text = role.zones;
cell.checkoutLbl.text = role.checkout;
//Set the accessory type.
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
//Configure the cell font
cell.zoneLbl.font = [UIFont fontWithName:#"eurostile-oblique" size:18.0];
cell.nameLbl.font = [UIFont fontWithName:#"eurostile-oblique" size:18.0];
cell.checkoutLbl.font = [UIFont fontWithName:#"eurostile-oblique" size:18.0];
return cell;
}
#pragma mark - Table view data source
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (tableView == self.searchDisplayController.searchResultsTableView)
{
return [self.searchResults count];
}
else
{
return [[[self.fetchedResultsController sections] objectAtIndex:section] numberOfObjects];
}
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 75.f;
}
#pragma mark -
#pragma mark Table view Methods
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
[self.tableView beginUpdates]; // Avoid NSInternalInconsistencyException
// Delete the role object that was swiped
Role *roleToDelete = [self.fetchedResultsController objectAtIndexPath:indexPath];
//NSLog(#"Deleting (%#)", roleToDelete.name);
[self.managedObjectContext deleteObject:roleToDelete];
[self.managedObjectContext save:nil];
// Delete the (now empty) row on the table
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[self performFetch];
[self.tableView endUpdates];
NSError *error = nil;
if (![[self fetchedResultsController] performFetch:&error]) {
NSLog(#"Error! %#",error);
abort();
}
}
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"Add Role Segue"])
{
// NSLog(#"Setting RolesTVC as a delegate of RoleDetailTVC");
RoleDetailTVC *roleDetailTVC = segue.destinationViewController;
roleDetailTVC.delegate = self;
// NSLog(#"Creating a new role and passing it to RoleDetailTVC");
Role *newRole = [NSEntityDescription insertNewObjectForEntityForName:#"Role"
inManagedObjectContext:self.managedObjectContext];
roleDetailTVC.role = newRole;
// Hide bottom tab bar in the detail view
roleDetailTVC.hidesBottomBarWhenPushed = YES;
}
else if ([segue.identifier isEqualToString:#"Role Detail Segue"])
{
// NSLog(#"Setting RolesTVC as a delegate of RoleDetailTVC");
RoleDetailTVC *roleDetailTVC = segue.destinationViewController;
roleDetailTVC.delegate = self;
// Store selected Role in selectedRole property
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
self.selectedRole = [self.fetchedResultsController objectAtIndexPath:indexPath];
// NSLog(#"Passing selected role (%#) to RoleDetailTVC", self.selectedRole.name);
roleDetailTVC.role = self.selectedRole;
// Hide bottom tab bar in the detail view
roleDetailTVC.hidesBottomBarWhenPushed = YES;
}
else {
// NSLog(#"Unidentified Segue Attempted!");
}
}
- (void)theSaveButtonOnTheRoleDetailTVCWasTapped:(RoleDetailTVC *)controller
{
// do something here like refreshing the table or whatever
// close the delegated view
[controller.navigationController popViewControllerAnimated:YES];
}
// Override to support conditional rearranging of the table view.
-(BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath: (NSIndexPath *) indexPath {
return YES;
}
-(BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath: (NSIndexPath *) indexPath {
return YES;
}
- (void)didReceiveMemoryWarning
{
UIAlertView *alertDialog;
alertDialog = [[UIAlertView alloc]
initWithTitle:NSLocalizedString(#"Memory Problem", #"Hafıza Problemi")
message: NSLocalizedString(#"Please Close Some Applications!", #"Hafıza Problemi")
delegate: nil
cancelButtonTitle: #"Ok"
otherButtonTitles: nil];
[alertDialog show];
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#end

Why is NSFetchedResultsController assigning fetched objects to multiple UITableView sections?

In another question of mine concerning the addition of an insert row in a UITableView backed by Core Data, I mentioned that my NSFetchedResultsController is assigning each object it fetches to a separate section in my UITableView. I assumed this was merely the default behavior, but Marcus S. Zarra said there might be something wrong with my configuration of the controller or my datasource delegate methods. I admit, my code feels a bit like Frankenstein with parts pulled from the Apple docs and numerous tutorials. This is my first program and my first time using Core Data, so please be gentle ;)
My table view controller's header is as follows:
#import <UIKit/UIKit.h>
#import "RubricAppDelegate.h"
#interface ClassList : UITableViewController {
NSMutableArray *classList;
NSFetchedResultsController *fetchedResultsController;
NSManagedObjectContext *managedObjectContext;
}
#property(nonatomic,retain) NSMutableArray *classList;
#property(nonatomic, retain) NSFetchedResultsController *fetchedResultsController;
#property(nonatomic, retain) NSManagedObjectContext *managedObjectContext;
- (IBAction) grade:(id)sender;
#end
My implementation file includes a bunch of dummy test data. I included that in case I am using incorrect methods for instantiating the Core Data objects. Basically, I want to know if my NSFetchedResultsController should not be returning my objects (in this case, instances of myClass) into separate sections. If so, what am I doing to create that problem?
The ultimate goal at the moment is for me to be able to add an insert cell at the top of my table (I know that putting it at the bottom is "standard," but I like how it looks in the apps that do it the other way around). You will notice my -tableView:editingStyleForRowAtIndexPath: sets the cell style of section 0 to insert, but of course I need to figure out how to start the listing of myClass.classTitle at cell 1 instead of cell 0 (which is why I want to determine if having each object assigned to its own section is normal).
Here is my implementation file:
#import "ClassList.h"
#import "ClassRoster.h"
#import "RubricAppDelegate.h"
#import "Student.h"
#import "myClass.h"
#implementation ClassList
#synthesize classList;
#synthesize fetchedResultsController;
#synthesize managedObjectContext;
#pragma mark -
#pragma mark View lifecycle
- (void)viewDidLoad {
[super viewDidLoad];
self.editing = YES;
RubricAppDelegate *appDelegate = (RubricAppDelegate *)[[UIApplication sharedApplication] delegate];
managedObjectContext = [appDelegate managedObjectContext];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"myClass" inManagedObjectContext:managedObjectContext];
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:entity];
//test data
myClass *newClass = (myClass *) [NSEntityDescription insertNewObjectForEntityForName:#"myClass" inManagedObjectContext:managedObjectContext];
newClass.classTitle = #"UFDN 1000";
NSNumber *ID = [NSNumber numberWithInt:1];
newClass.classID = ID;
Student *newStudent = (Student *) [NSEntityDescription insertNewObjectForEntityForName:#"Student" inManagedObjectContext:managedObjectContext];
newStudent.classID = ID;
newStudent.studentName = #"Andy Albert";
newStudent.studentUsername = #"albera";
[newClass addStudentsObject:newStudent];
newStudent.classID = ID;
newStudent.studentName = #"Bob Dole";
newStudent.studentUsername = #"doleb";
[newClass addStudentsObject:newStudent];
newStudent.classID = ID;
newStudent.studentName = #"Chris Hanson";
newStudent.studentUsername = #"hansoc";
[newClass addStudentsObject:newStudent];
myClass *newClass2 = (myClass *) [NSEntityDescription insertNewObjectForEntityForName:#"myClass" inManagedObjectContext:managedObjectContext];
newClass2.classTitle = #"UFDN 3100";
ID = [NSNumber numberWithInt:2];
newClass2.classID = ID;
newStudent.classID = ID;
newStudent.studentName = #"Danny Boy";
newStudent.studentUsername = #"boyd";
[newClass2 addStudentsObject:newStudent];
newStudent.classID = ID;
newStudent.studentName = #"James Matthews";
newStudent.studentUsername = #"matthj";
[newClass2 addStudentsObject:newStudent];
newStudent.classID = ID;
newStudent.studentName = #"Aaron Todds";
newStudent.studentUsername = #"toddsa";
[newClass2 addStudentsObject:newStudent];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"classID" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
[request setSortDescriptors:sortDescriptors];
[sortDescriptor release];
NSError *error;
fetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:request
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:#"classTitle" cacheName:nil];
[fetchedResultsController performFetch:&error];
UIBarButtonItem *gradeButton = [[UIBarButtonItem alloc]
initWithTitle:#"Grade"
style:UIBarButtonItemStylePlain
target:self
action:#selector(grade:)];
self.navigationItem.rightBarButtonItem = gradeButton;
[gradeButton release];
}
- (IBAction) grade:(id)sender {
}
#pragma mark -
#pragma mark Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
NSLog(#"Number of sections = %d", [[fetchedResultsController sections] count]);
return ([[fetchedResultsController sections] count]);
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
id <NSFetchedResultsSectionInfo> myClass = [[fetchedResultsController sections] objectAtIndex:section];
NSLog(#"Number of classes = %d", [myClass numberOfObjects]);
return [myClass numberOfObjects];
}
// Customize the appearance of table view cells.
- (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] autorelease];
myClass *theClass = [fetchedResultsController objectAtIndexPath:indexPath];
NSLog(#"Class name is: %#", theClass.classTitle);
cell.textLabel.text = theClass.classTitle;
}
return cell;
}
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == 0) {
return UITableViewCellEditingStyleInsert;
}
else return UITableViewCellEditingStyleDelete;
}
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
myClass *result = (myClass *)[fetchedResultsController objectAtIndexPath:indexPath];
[managedObjectContext deleteObject:result];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:YES];
}
else if (editingStyle == UITableViewCellEditingStyleInsert) {
}
}
#pragma mark -
#pragma mark Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Navigation logic may go here. Create and push another view controller.
/*
<#DetailViewController#> *detailViewController = [[<#DetailViewController#> alloc] initWithNibName:#"<#Nib name#>" bundle:nil];
// ...
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:detailViewController animated:YES];
[detailViewController release];
*/
}
#pragma mark -
#pragma mark Memory management
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Relinquish ownership any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Relinquish ownership of anything that can be recreated in viewDidLoad or on demand.
// For example: self.myOutlet = nil;
}
- (void)dealloc {
[classList release];
[super dealloc];
}
#end
My RubricAppDelegate is essentially identical to the Apple documentation for setting up Core Data NSManagedObjectContext, NSPersistentStoreCoordinator, etc. However, if you think there might be a problem in there, just let me know and I'll post it.
Edit: I forgot to mention two reasons I know each object is being assigned to a different section.
1) NSLog(#"Number of sections = %d", [[fetchedResultsController sections] count]); inside of -numberOfSectionsInTableView: returns the number of myClass objects I have.
2) If I set -numberOfSectionsInTableView: to return 1, my table only displays one object and cuts the rest out.
You have sections because you tell the fetched results controller to create them by passing a non-Nil value for sectionNameKeyPath: when you initialize the FRC.
Change:
fetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:request
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:#"classTitle" cacheName:nil];
...to:
fetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:request
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil cacheName:nil];
... and the sections will go away. Otherwise, the FRC will create one section for each value of the classTitle attribute in the store. If each myClass instance has a different value for classTitle each instance will have its own section in the tableview.

Resources