Populating Core Data into Table View - ios

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];
});
}

Related

CoreData + UIViewController + UITableView

I would like to explain the most detailed the problem I'm facing.
Previously instead of the UIViewController i had directly a UITableViewController, the problem is that now i need to show more content in the view that just the tableview so i created a new view. Now i have a UIViewController that has: UIScrollView. Enside of it there are: 1. UIView + 1 UITableView
I'm using AutoLayout. And is working fine. The problem I'm facing is that since i switched to use this view I'm unable to display content in the table with coredata. I was able to do it without coredata but not with it.
In the interfacebuilder i set datasource and delegate to this UIViewController.
Header:
#import <UIKit/UIKit.h>
#interface ResultsExtraVC : UIViewController <UITableViewDelegate, UITableViewDataSource>
#property(nonatomic, strong) AGTCoreDataStack *model;
#property (strong, nonatomic) IBOutlet UILabel *labelRaceTime;
#property (strong, nonatomic) IBOutlet UILabel *labelTrackCondition;
#property (strong, nonatomic) IBOutlet UILabel *labelRaceStarttime;
#property (strong, nonatomic) IBOutlet UILabel *labelRaceMode;
#property (strong, nonatomic) IBOutlet UIScrollView *scrollView;
#property (weak, nonatomic) IBOutlet UITableView *tableView;
#property (strong, nonatomic) IBOutlet UIView *raceInfoView;
#property(nonatomic, strong)NSNumber *eventID;
#property(nonatomic, strong)NSNumber *sectionID;
#property(nonatomic, strong)NSNumber *elementID;
#property(nonatomic, strong)NSNumber *categoryID;
#end
Implementation:
#import "ResultsExtraVC.h"
#import "ResultsExtraCellVC.h"
#import "Result.h"
#import "Element.h"
#import "Section.h"
#import <CoreData/CoreData.h>
#interface ResultsExtraVC () <NSFetchedResultsControllerDelegate>
//Core data
#property (nonatomic,strong) NSManagedObjectContext* managedObjectContext;
#property (nonatomic, retain) NSFetchedResultsController *fetchedResultsController;
#end
static NSString * const cellIdentifier = #"resultsExrtaAltCell";
#implementation ResultsExtraVC
#synthesize managedObjectContext;
#synthesize fetchedResultsController = _fetchedResultsController;
#synthesize eventID, sectionID, elementID, categoryID, model;
- (void)viewDidLoad {
[super viewDidLoad];
managedObjectContext = self.model.context;
[self getResults];
NSLog(#"ElementName: %#", [self getElementName]);
NSLog(#"SectionName: %#", [self getSectionName]);
[self configureHeatInfo];
// Do any additional setup after loading the view.
}
- (void)viewDidUnload
{
[super viewDidUnload];
self.fetchedResultsController = nil;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - View Configuration
-(void) configureHeatInfo{
_scrollView.backgroundColor = [UIColor cyanColor];
_scrollView.translatesAutoresizingMaskIntoConstraints = NO;
_labelRaceTime.translatesAutoresizingMaskIntoConstraints = NO;
_labelRaceTime.text = #"Race time:\n10:00";
_labelRaceMode.translatesAutoresizingMaskIntoConstraints = NO;
_labelRaceMode.text = #"Mode: Groupstart";
_labelTrackCondition.translatesAutoresizingMaskIntoConstraints = NO;
_labelTrackCondition.text = #"Track Condition: Dry";
_labelRaceStarttime.translatesAutoresizingMaskIntoConstraints = NO;
_labelRaceStarttime.text = #"Starttime: 12.03.2016 11:09:10";
//self.tableView.translatesAutoresizingMaskIntoConstraints = NO;
/*self.tableView.dataSource = self;
self.tableView.delegate = self;*/
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [[self.fetchedResultsController sections] count];
}
/*- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 20;
}*/
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
id sectionInfo = [[_fetchedResultsController sections] objectAtIndex:section];
#ifndef NDEBUG
NSLog(#"%s (line:%d) - Rows: %lu",__PRETTY_FUNCTION__,__LINE__, (unsigned long)[sectionInfo numberOfObjects]);
#endif
// Return the number of rows in the section.
return [sectionInfo numberOfObjects];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
return [self basicCellAtIndexPath:indexPath];
}
- (ResultsExtraCellVC *)basicCellAtIndexPath:(NSIndexPath *)indexPath {
ResultsExtraCellVC *cell = [self.tableView dequeueReusableCellWithIdentifier:cellIdentifier];
// Using a cell identifier will allow your app to reuse cells as they come and go from the screen.
if (cell == nil) {
#ifndef NDEBUG
NSLog(#"%s (line:%d)",__PRETTY_FUNCTION__,__LINE__);
#endif
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"ResultsExtraCell" owner:self options:nil];
cell = (ResultsExtraCellVC *)[nib objectAtIndex:0];
}
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
- (void)configureCell:(ResultsExtraCellVC *)cell atIndexPath:(NSIndexPath *)indexPath {
#ifndef NDEBUG
NSLog(#"%s (line:%d)",__PRETTY_FUNCTION__,__LINE__);
#endif
Result *result = [_fetchedResultsController objectAtIndexPath:indexPath];
[cell.cellRankNr setText:[NSString stringWithFormat:#"%#",[result endposition]]];
[cell.cellName setText:[NSString stringWithFormat:#"%# %# (%#)",[result pilotprename], [result pilotname], [result carnumber]]];
[cell.cellResult setText:[NSString stringWithFormat:#"%#",NSLocalizedFormatString(#"LapsXnTimeX",[result rlaps],[result rendtime])]];
[cell.cellRace setText:[NSString stringWithFormat:#"%#",NSLocalizedFormatString(#"BestTimeXnAvgTimeX",[result rbesttime], [result rmediumtime])]];
}
-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([tableView respondsToSelector:#selector(setSeparatorInset:)])
{
[tableView setSeparatorInset:UIEdgeInsetsZero];
}
if ([tableView respondsToSelector:#selector(setLayoutMargins:)])
{
[tableView setLayoutMargins:UIEdgeInsetsZero];
}
if ([cell respondsToSelector:#selector(setLayoutMargins:)])
{
[cell setLayoutMargins:UIEdgeInsetsZero];
}
}
-(NSString *)getElementName{
NSString *elementName = #"";
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:#"Elements" inManagedObjectContext:managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init] ;
[request setEntity:entityDescription];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(eventid = %# AND sectionid = %# AND elementid = %# AND categoryid= %#)",eventID, sectionID, elementID, categoryID];
[request setPredicate:predicate]; //added this line later
NSArray *array = [managedObjectContext executeFetchRequest:request error:nil];
if([array count]> 0){
Element *element = [array lastObject];
elementName = [element name];
}
return elementName;
}
-(NSString *)getSectionName{
NSString *sectiontName = #"";
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:#"Sections" inManagedObjectContext:managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init] ;
[request setEntity:entityDescription];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(eventid = %# AND sectionid = %#)",eventID, sectionID];
[request setPredicate:predicate]; //added this line later
NSArray *array = [managedObjectContext executeFetchRequest:request error:nil];
if([array count]> 0){
Section *section = [array lastObject];
sectiontName = [section name];
}
return sectiontName;
}
-(void)getResults{
// Initialize Fetch Request
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Results"];
// Add Sort Descriptors
//[fetchRequest setSortDescriptors:#[[NSSortDescriptor sortDescriptorWithKey:#"endposition" ascending:YES]]];
//Predicate
NSPredicate *commentsPredicate = [NSPredicate predicateWithFormat:#"(eventid = %# AND sectionid = %# AND elementid = %#)",eventID, sectionID, elementID];
[fetchRequest setPredicate:commentsPredicate]; //added this line later
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"endposition"
ascending:YES];
NSArray *sortDescriptors = #[sortDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
// Initialize Fetched Results Controller
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
// Configure Fetched Results Controller
[self.fetchedResultsController setDelegate:self];
// Perform Fetch
NSError *error = nil;
[self.fetchedResultsController performFetch:&error];
if (error) {
NSLog(#"Unable to perform fetch.");
NSLog(#"%#, %#", error, error.localizedDescription);
}
}
#pragma mark -
#pragma mark Fetched Results Controller Delegate Methods
- (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 {
switch (type) {
case NSFetchedResultsChangeInsert: {
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
}
case NSFetchedResultsChangeDelete: {
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
}
case NSFetchedResultsChangeUpdate: {
[self configureCell:(ResultsExtraCellVC *)[self.tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;
}
case NSFetchedResultsChangeMove: {
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
}
#end
My View looks like this:
I tried to follow some tutorials as this is the first time i'm doing this with CoreData, previously i always used only UITableViewController.
I followed this: http://code.tutsplus.com/tutorials/core-data-from-scratch-more-nsfetchedresultscontroller--cms-21777
And also checked: CoreData TableView inside UIViewController
But with no success.
I would be great if someone can help me with the problem.
Thanks in advance
Maybe in code line
// Initialize Fetched Results Controller
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
the sectionNameKeyPath is missing? It must be one of the NSSortDescriptor keys.

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.

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

UITableView delete/add row causes CoreData: Serious Application Error if another object has been selected in the MasterView of a SplitViewController

Update 18/3 #2. I've started counting beginUpdates and EndUpdates to make sure they're even. Right before there's an exception, they get out of sync. Not sure why though.
UPDATE 18/3: I think I've found the problem, but I'm not sure if I know how to fix it. After experimenting for a couple hours, I found I could only crash the app when I had selected more than one item in the master tableview of the svc during that session.
When another item is selected in the master tableview, the detail table view gets a new object set and refreshtables is called EVEN IF it's halfway through an update/move/delete/insert cycle. Hence the problem; I think it has instructions to update the old story object even though a new story object has been set on the detailviewcontroller.
How can I get the animation/coredata/tableview updates to be processed completely before a new story is set in the detail view? I save changes to the managedObjectContext when setEditting == NO. I'm guessing I need to make a custom setStory setter that processes all the updates to the UITableView/CoreData set before accepting the new object?
This code gets called on the master tableview controller of the svc in didSelectRowAtIndexPath:
[detailViewController setStory:storySet]; //where storySet is the new story object
[detailViewController refreshTables];
I have intermittent errors on attempting to delete the row where action won't animate and the application essentially hangs with the following error (the row is deleted from the CoreData set though). This happens if I have chosen more than one row from the master tableview controller in the svc in one session.
After some googling I thought it might be a problem with (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id) which is called after an update to animate the changes the user has made.
How can I fix these intermittent bugs?
16/3I've tried to really simplify my code. I've removed all calls to the Managed Object Context and put them in setEditing, removed superfluous [self.tableview reloadData] and [self.tableview setneedsdisplay] and invalidated the 'reordering' bool completely (it's still in the code, but it's always set to NO, so it makes no difference). The UITableView is more stable then ever, but I still manage to get intermittent errors on delete (and occasionally on add) - it seems to take a while to crash but it still will.**
15/3: I think that it has something to do with the CoreData / UITableView being out of sync - CoreData thinks it has less or more than the UITableView
There's no problem with the CoreData set, it's just the animation/UI side of things (things get removed)
It's intermittent, only on some deletes
After some help from railwayparade I implemented NSFetchedResultsChangeMove in didChangeObject: which fixed the moving error but not the delete errors.
Can anyone see anything I've missed or that I can check? Happy to give more information if that helps solve the problem.
Apologies for the obscene amount of code posted here.
The Error:
CoreData: error: Serious application error. An exception was caught
from the delegate of NSFetchedResultsController during a call to
-controllerDidChangeContent:. attempt to insert row 3 into section 0, but there are only 3 rows in section 0 after the update with userInfo
(null)
//
// MakeSentenceTableViewController.h
// StoryBot
//
// Created by Glen Storey on 25/10/10.
// Copyright 2010 Glen Storey. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "AddStoryItem.h"
#import "Story.h"
#import "Sentence.h"
#interface MakeSentenceTableViewController : UITableViewController <NSFetchedResultsControllerDelegate, AddStoryItemDelegate, UINavigationControllerDelegate, UIImagePickerControllerDelegate, UITextFieldDelegate, UIPopoverControllerDelegate> {
NSManagedObjectContext *managedObjectContext;
NSFetchedResultsController *fetchedResultsController;
UIPopoverController *popoverController;
UIBarButtonItem *playButtonItem;
UIBarButtonItem *addButtonItem;
BOOL reordering;
BOOL insert;
BOOL delete;
BOOL move;
int beginUpdatesCount;
int endUpdatesCount;
}
#property (nonatomic, retain) Story *story;
#property (nonatomic, retain) NSManagedObjectContext *managedObjectContext;
#property (nonatomic, retain) UIBarButtonItem *playButtonItem;
#property (nonatomic, retain) UIBarButtonItem *addButtonItem;
#property BOOL reordering, insert, delete, move;
-(IBAction)createStoryModal:(id)sender;
-(void)refreshTables;
-(IBAction)pushShare:(id)sender;
#end
//
// MakeSentenceTableViewController.m
//
//
// Created by Glen Storey on 25/10/10.
// Copyright 2010 Glen Storey. All rights reserved.
//
#import "MakeSentenceTableViewController.h"
#import "ShareViewController.h"
#import "StoryBotAppDelegate.h"
#implementation MakeSentenceTableViewController
#synthesize story, managedObjectContext, addButtonItem, playButtonItem, reordering, insert, delete, move;
-(void)addStoryItemAction:(NSString*)text order:(NSNumber*)order image:(NSString*)image thumb:(NSString*)thumb{
NSLog(#"Text: %#, Order: %#, Image: %#.", text, order, image);
NSLog(#"beginUpdatesCount: %d vs. endUpdatescount: %d", beginUpdatesCount, endUpdatesCount);
NSSet *sentences = [story sentences];
NSNumber *maxOrder = [sentences valueForKeyPath:#"#max.order"];
NSLog(#"maxOrder: %#", maxOrder);
if(maxOrder == 0){
maxOrder = [[NSNumber alloc] initWithInteger: 0];
}
//make a new sentence!
Sentence *sentence = [NSEntityDescription insertNewObjectForEntityForName:#"Sentence"
inManagedObjectContext:managedObjectContext];
[sentence setText: text];
[sentence setImage: image];
[sentence setThumb: thumb];
[sentence setBelongsTo: story];
if([maxOrder intValue] >= 1 ){
[sentence setOrder: [[NSNumber alloc] initWithInteger:[maxOrder intValue]+1]];
}else{
[sentence setOrder: [[NSNumber alloc] initWithInteger:1]];
}
NSMutableSet *mutableSet = [[NSMutableSet alloc] initWithSet:sentences];
[mutableSet addObject:sentence];
//NSLog(#"sentences before setWithSet %#", mutableSet);
sentences = [[NSSet alloc] initWithSet: mutableSet];
//NSLog(#"sentences after setWithSet %#", sentences);
[story setSentences:sentences];
//NSError *error;
//BOOL isSaved = [managedObjectContext save:&error];
//NSLog(#"isSaved? %#", (isSaved ? #"YES" :#"NO ") );
//if (!isSaved) {
//NSLog(#"%#:%s Error saving context: %#", [self class], _cmd, [error localizedDescription]);
//Don't worry about this warning - just rem it out when finished (just a log)
// return;
//}
[sentences release];
[mutableSet release];
//[self.tableView reloadData];
//[self.tableView setNeedsDisplay];
[self dismissModalViewControllerAnimated:YES];
}
#pragma mark -
#pragma mark View lifecycle
-(id)initWithNibName:(NSString*)name bundle:(NSBundle*)bundle;
{
self = [super initWithNibName:name bundle:bundle];
if (self) {
self.title = #"My Stories";
addButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd
target:self
action:#selector(createStoryModal:)];
playButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemPlay
target:self
action:#selector(pushShare:)];
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
[addButtonItem setEnabled:NO];
[playButtonItem setEnabled:NO];
}
NSArray* toolbarItems = [NSArray arrayWithObjects:
addButtonItem,
[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace
target:nil
action:nil],
playButtonItem,
nil];
[toolbarItems makeObjectsPerformSelector:#selector(release)];
self.toolbarItems = toolbarItems;
//NSLog(#"self: %# self.toolbarItems: %#", self, self.toolbarItems);
//Do I need to release UIBarButtonItems?
NSLog(#"initWithNibName:");
}
return self;
}
-(void)refreshTables{
//use this to refresh for new table content. Also calls self.tableview reloadData so you don't need to call it afterward.
NSLog(#"===================================refreshTables");
NSLog(#"story object %#", story);
if (managedObjectContext == nil)
{
NSLog(#"managedObjectContext == nil");
managedObjectContext = [(StoryBotAppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
NSLog(#"After managedObjectContext: %#", managedObjectContext);
}else{
NSLog(#"managedObjectContext != nil");
}
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Sentence" inManagedObjectContext:managedObjectContext];
[request setEntity:entity];
//sorting stuff:
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"order" ascending: YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects: sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
[sortDescriptors release];
[sortDescriptor release];
NSPredicate *predicateTitle = [NSPredicate predicateWithFormat:#"belongsTo=%#",story];
[request setPredicate :predicateTitle];
NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init] autorelease];
[dateFormatter setDateFormat:#"EEE, d MMM yyyy HH:mm:ss"];
NSString *dateString = [dateFormatter stringFromDate:[story creationDate]];
NSLog(#"dateString: %#", dateString);
fetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:request managedObjectContext:managedObjectContext
sectionNameKeyPath:nil cacheName:dateString];
fetchedResultsController.delegate = self;
[request release];
NSError *error;
[fetchedResultsController performFetch:&error];
[self.tableView reloadData];
}
- (void)viewDidLoad {
[super viewDidLoad];
self.title = #"My Story";
NSLog(#"Passed Story Object: %#", story);
//NSLog(#"managedObjectContext: %#", managedObjectContext);
//Need a predicate for belongsTo here.
self.tableView.rowHeight = 50;
if(story != NULL){
if (managedObjectContext == nil)
{
NSLog(#"managedObjectContext == nil");
managedObjectContext = [(StoryBotAppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
NSLog(#"After managedObjectContext: %#", managedObjectContext);
}else{
NSLog(#"managedObjectContext != nil");
}
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Sentence" inManagedObjectContext:managedObjectContext];
[request setEntity:entity];
//sorting stuff:
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"order" ascending: YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects: sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
[sortDescriptors release];
[sortDescriptor release];
NSPredicate *predicateTitle = [NSPredicate predicateWithFormat:#"belongsTo=%#",story];
[request setPredicate :predicateTitle];
fetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:request managedObjectContext:managedObjectContext
sectionNameKeyPath:nil cacheName:nil];
fetchedResultsController.delegate = self;
[request release];
NSError *error;
[fetchedResultsController performFetch:&error];
}
#pragma mark -
#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.
NSArray *sections = [fetchedResultsController sections];
NSInteger count = 0;
if ([sections count]){
id <NSFetchedResultsSectionInfo> sectionInfo = [sections objectAtIndex:section];
count = [sectionInfo numberOfObjects];
}
NSLog(#"# of rows in section %d", count);
return count;
}
// 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];
}
// Configure the cell...
Sentence *sentenceAtCell = [fetchedResultsController objectAtIndexPath:indexPath];
//NSLog(#"sentenceAtCell: %#", sentenceAtCell);
cell.textLabel.text = [sentenceAtCell text];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *uniquePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:[sentenceAtCell thumb]];
// This should crop it as you want - you've just got to create cropRect.
UIImage *largeImage = [UIImage imageWithContentsOfFile: uniquePath];
CGRect cropRect = CGRectMake(0, 0, 66, 50);
/*if ([[UIScreen mainScreen] respondsToSelector:#selector(scale)]) {
if ([[UIScreen mainScreen] scale] == 2) {
// running an iPhone 4 or equiv. res device.
cropRect = CGRectMake(15, 14, 100, 75);
}
}*/
CGImageRef imageRef = CGImageCreateWithImageInRect([largeImage CGImage], cropRect);
cell.imageView.image = [UIImage imageWithCGImage: imageRef];
CGImageRelease(imageRef);
//NSLog(#"indexPath: %#", indexPath);
return cell;
}
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
NSLog(#"Delete row");
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectoryPath = [paths objectAtIndex:0];
// 1. Look at the sentence we're about to delete.
Sentence *sentenceRetire = [fetchedResultsController objectAtIndexPath:indexPath];
// 2. Does it have an order of 0?
NSLog(#"=================== sentenceRetire: %#", sentenceRetire);
if([[sentenceRetire order] intValue] == 0){
// YES: Is there another sentence in this story?
Story *storyRetire = [sentenceRetire belongsTo];
NSSet *sentencesInRetiredSentenceSet = [storyRetire sentences];
if ([sentencesInRetiredSentenceSet count] > 1){
// YES: Set the sentence with the smallest order to an order of 0
// then delete the sentence + files
NSPredicate *predicateTitle = [NSPredicate predicateWithFormat:#"order>0"];
NSSet *sentencesWithPotentialToBeTitle = [sentencesInRetiredSentenceSet filteredSetUsingPredicate:predicateTitle];
NSNumber *minOrder = [sentencesWithPotentialToBeTitle valueForKeyPath:#"#min.order"];
NSPredicate *predicateOrder = [NSPredicate predicateWithFormat:#"order=%d",[minOrder intValue]];
NSSet *sentenceWithPotentialToBeTitle = [sentencesWithPotentialToBeTitle filteredSetUsingPredicate:predicateOrder];
//note the sentence (singular) not sentences. This is the sentence who's order needs to be updated.
NSLog(#"setenceWithPotentialToBeTitle (check order isn't null on crash): %#", sentenceWithPotentialToBeTitle);
Sentence *sentenceToPromote = [sentenceWithPotentialToBeTitle anyObject];
//now we know which sentence to promote we can delete the sentence & Files.
[managedObjectContext deleteObject:[fetchedResultsController objectAtIndexPath:indexPath]];
NSString *imageTrash = [documentsDirectoryPath stringByAppendingPathComponent:(NSString*)[sentenceRetire image]];
NSString *thumbTrash = [documentsDirectoryPath stringByAppendingPathComponent:[sentenceRetire thumb]];
NSLog(#"About to delete these files: %#, %#", imageTrash, thumbTrash);
[fileManager removeItemAtPath:imageTrash error:NULL];
[fileManager removeItemAtPath:thumbTrash error:NULL];
//and promote the new title.
[sentenceToPromote setOrder:[[NSNumber alloc] initWithInteger:0]];
}else{
// NO: We're deleting this story
// Delete the files!
[managedObjectContext deleteObject:storyRetire];
NSString *imageTrash = [documentsDirectoryPath stringByAppendingPathComponent:(NSString*)[sentenceRetire image]];
NSString *thumbTrash = [documentsDirectoryPath stringByAppendingPathComponent:[sentenceRetire thumb]];
//NSLog(#"About to delete these files: %#, %#", imageTrash, thumbTrash);
[fileManager removeItemAtPath:imageTrash error:NULL];
[fileManager removeItemAtPath:thumbTrash error:NULL];
//Pop back.
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
NSLog(#"last sentence in story - delete that story and point, somewhere!");
//probably delete the sentece to clear the table, then delete the story using storyRetire
} else{
[self.navigationController popViewControllerAnimated:YES];
}
}
}else{
// NO: Delete the Sentence Object.
[managedObjectContext deleteObject:[fetchedResultsController objectAtIndexPath:indexPath]];
NSString *imageTrash = [documentsDirectoryPath stringByAppendingPathComponent:(NSString*)[sentenceRetire image]];
NSString *thumbTrash = [documentsDirectoryPath stringByAppendingPathComponent:[sentenceRetire thumb]];
//NSLog(#"About to delete these files: %#, %#", imageTrash, thumbTrash);
[fileManager removeItemAtPath:imageTrash error:NULL];
[fileManager removeItemAtPath:thumbTrash error:NULL];
}
// Save the context.
//NSError *error;
//if (![managedObjectContext save:&error]) {
/*
Replace this implementation with code to handle the error appropriately.
abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
*/
// NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
// abort();
//}
}
}
- (void)setEditing:(BOOL)editing animated:(BOOL)animated
{
[super setEditing:editing animated:animated];
if (!editing) {
//save here
NSError *error;
BOOL isSaved = [managedObjectContext save:&error];
NSLog(#"isSaved? %# ======================================", (isSaved ? #"YES" :#"NO ") );
}
}
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {
//this implementation is from here: http://www.cimgf.com/2010/06/05/re-ordering-nsfetchedresultscontroller/
NSMutableArray *things = [[fetchedResultsController fetchedObjects] mutableCopy];
// Grab the item we're moving.
NSManagedObject *thing = [fetchedResultsController objectAtIndexPath:fromIndexPath];
// Remove the object we're moving from the array.
[things removeObject:thing];
// Now re-insert it at the destination.
[things insertObject:thing atIndex:[toIndexPath row]];
// All of the objects are now in their correct order. Update each
// object's displayOrder field by iterating through the array.
int i = 0;
for (NSManagedObject *mo in things)
{
[mo setValue:[NSNumber numberWithInt:i++] forKey:#"order"];
}
NSLog(#"things: %#", things);
[things release], things = nil;
//reordering = YES;
//NSLog(#"moveRowAtIndexPath: IS reordering");
//NSError *error;
//[managedObjectContext save:&error];
}
#pragma mark -
#pragma mark Table view delegate
/**
Delegate methods of NSFetchedResultsController to respond to additions, removals and so on.
*/
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
// The fetch controller is about to start sending change notifications, so prepare the table view for updates.
NSLog(#"controllerWillChangeContent: BeginUpdates");
[self.tableView beginUpdates];
beginUpdatesCount++;
NSLog(#"====================beginUpdates was just incremented");
NSLog(#"beginUpdatesCount %d", beginUpdatesCount);
NSLog(#"endUpdatesCount %d", endUpdatesCount);
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
NSLog(#"didChangeObject: %#", anObject);
UITableView *tableView;
tableView = self.tableView;
switch(type) {
case NSFetchedResultsChangeInsert:
NSLog(#"ResultsChangeInsert:");
insert = YES;
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
NSLog(#"ResultsChangeDelete:");
delete = YES;
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeMove:
NSLog(#"ResultsChangeMove:");
move = YES;
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationNone];
break;
default:
NSLog(#"switch problem - default");
}
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
NSLog(#"didChangeSection:");
switch(type) {
case NSFetchedResultsChangeInsert:
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
NSLog(#"didChangeContent");
// The fetch controller has sent all current change notifications, so tell the table view to process all updates.
NSLog(#"almost endUpdates==============================================");
if(delete){
NSLog(#"endUpdates delete");
delete = NO;
}
if(move){
NSLog(#"endUpdates move");
move = NO;
}
if(insert){
NSLog(#"endUpdates insert");
insert = NO;
}
[self.tableView endUpdates];
endUpdatesCount++;
NSLog(#"====================endUpdates was just incremented");
NSLog(#"endUpdatesCount %d", endUpdatesCount);
NSLog(#"beginUpdatesCount %d", beginUpdatesCount);
NSLog(#"endUpdates finished");
}
#pragma mark -
#pragma mark Memory management
- (void)dealloc {
NSLog(#"Dealloc Sentence");
//[fliteEngine stopTalking];
//[fliteEngine release];
addButtonItem = nil;
playButtonItem = nil;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
//It doesn't seem to get allocated because it's not set on the iPhone.
[addButtonItem release];
[playButtonItem release];
}
[story release];
[fetchedResultsController release];
[super dealloc];
}
#end
Update 18/3 #3 I read this question about a fetchedResultsController needing to have its delegate set to nil before creating a new one. I put
detailViewController.fetchedResultsController = nil;
detailViewController.fetchedResultsController.delegate = nil;
in the SVC master view on didselectrowatindexpath and the problem has stopped. RefreshTables created a new fetchedResultsController every time a row was selected in the master view, and I think they were still interfering even when a new story object was selected.
You missed implementing NSFetchedResultsChangeMove in didChangeObject:
Here is my code which does the same thing:
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
sectionHeadersChanged=NO;
[self._tableView beginUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
switch(type) {
case NSFetchedResultsChangeInsert:
[self._tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex]
withRowAnimation:UITableViewRowAnimationAutomatic];
break;
case NSFetchedResultsChangeDelete:
[self._tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex]
withRowAnimation:UITableViewRowAnimationAutomatic];
break;
}
sectionHeadersChanged=YES;
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath {
UITableView *tableView = self._tableView;
switch(type) {
case NSFetchedResultsChangeInsert:
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
withRowAnimation:UITableViewRowAnimationAutomatic];
break;
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationAutomatic];
break;
case NSFetchedResultsChangeUpdate:
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationBottom];
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
[self._tableView endUpdates];
//reload all sections
if(sectionHeadersChanged==YES){
[self._tableView reloadSections:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, self._tableView.numberOfSections)] withRowAnimation:UITableViewRowAnimationNone];
}
}

whats wrong with this code? not populating table from core data!! iPad

Please help me to fix this issue (Im getting crazy now!), My project is saving the data properly to the sqlite db in coredata, as checked in a SQLite grapical manager,but I dont seem to be able to show it in a table view, I need my table inside my viewController (small table not whole screen),
If you feel generous please check my little sample project
Here Thankyou!
here the code of the *viewcontroller.h
(Please note that I included a IBoutlet for the table view as tableView is not an instance in UIViewController!) was this done properly?,
also, I get 7 warnings where i call the tableView> Local declaration of 'tableView' hides instance variable
#import <UIKit/UIKit.h>
#interface CoreDataEnsaViewController : UIViewController
<UITableViewDelegate, UITableViewDataSource, NSFetchedResultsControllerDelegate> //va controller delegate??
{
UITextField *name;
UITextField *address;
UITextField *phone;
UILabel *status;
//la table
//NSMutableArray *array;
//la tabla??
UITableView *tableView;
NSFetchedResultsController *_fetchedResultsController;
NSManagedObjectContext *_context;
}
#property (nonatomic, retain) IBOutlet UITextField *name;
#property (nonatomic, retain) IBOutlet UITextField *address;
#property (nonatomic, retain) IBOutlet UITextField *phone;
#property (nonatomic, retain) IBOutlet UILabel *status;
#property (nonatomic, retain) IBOutlet UITableView *tableView;
#property (nonatomic, retain) NSFetchedResultsController *fetchedResultsController;
#property (nonatomic, retain) NSManagedObjectContext *context;
- (IBAction) saveData;
- (IBAction) findContact;
- (IBAction) showbtn:(id) sender;
#end
and here the *viewController.m
#import "CoreDataEnsaViewController.h"
#import "CoreDataEnsaAppDelegate.h"
#import "ShowViewController.h"
#import "Contacts.h"
#implementation CoreDataEnsaViewController
ShowViewController *showView;
#synthesize name, address, phone, status, tableView;
#synthesize context = _context;
#synthesize fetchedResultsController = _fetchedResultsController;
-(IBAction) showbtn:(id) sender {
showView = [[ShowViewController alloc] initWithNibName:#"ShowViewController" bundle:nil];
//anima
[UIView beginAnimations:#"flipping view" context:nil];
[UIView setAnimationDuration:1];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationTransition:UIViewAnimationTransitionCurlDown
forView:self.view cache:YES];
[self.view addSubview:showView.view];
[UIView commitAnimations];
}
- (void) saveData
{
CoreDataEnsaAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSManagedObject *newContact;
newContact = [NSEntityDescription insertNewObjectForEntityForName:#"Contacts" inManagedObjectContext:context];
[newContact setValue:name.text forKey:#"name"];
[newContact setValue:address.text forKey:#"address"];
[newContact setValue:phone.text forKey:#"phone"];
name.text = #"";
address.text = #"";
phone.text = #"";
NSError *error;
[context save:&error];
status.text = #"Contact saved";
}
- (void) findContact
{
CoreDataEnsaAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSEntityDescription *entityDesc = [NSEntityDescription entityForName:#"Contacts" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDesc];
NSPredicate *pred = [NSPredicate predicateWithFormat:#"(name = %#)", name.text];
[request setPredicate:pred];
NSManagedObject *matches = nil;
NSError *error;
NSArray *objects = [context executeFetchRequest:request error:&error];
if ([objects count] == 0) {
status.text = #"No matches";
} else {
matches = [objects objectAtIndex:0];
address.text = [matches valueForKey:#"address"];
phone.text = [matches valueForKey:#"phone"];
status.text = [NSString stringWithFormat:#"%d matches found", [objects count]];
}
[request release];
}
- (NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Contacts" inManagedObjectContext:_context];
[fetchRequest setEntity:entity];
//NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:#"details.closeDate" ascending:NO];
//[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
[fetchRequest setFetchBatchSize:20];
NSFetchedResultsController *theFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:_context sectionNameKeyPath:nil cacheName:#"Root"];
self.fetchedResultsController = theFetchedResultsController;
_fetchedResultsController.delegate = self;
[fetchRequest release];
[theFetchedResultsController release];
return _fetchedResultsController;
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
}
- (BOOL)shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation)interfaceOrientation {
// Overriden to allow any orientation.
return YES;
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
self.name = nil;
self.address = nil;
self.phone = nil;
self.status = nil;
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
//tabla
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section {
id <NSFetchedResultsSectionInfo> sectionInfo = [[_fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
//return [array count];
//return [[fetchedResultsController sections] count];
}
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
Contacts *info = [_fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = info.name;
cell.detailTextLabel.text = [NSString stringWithFormat:#"%#, %#", info.address, info.phone];
}
//---insert individual row into the table view---
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
//---try to get a reusable cell---
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
//---create new cell if no reusable cell is available---
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
// Set up the cell...
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
//tabla end
- (void)dealloc {
[name release];
[address release];
[phone release];
[status release];
self.fetchedResultsController = nil;
self.context = nil;
[super dealloc];
}
#pragma mark NSFetchedResultsControllerDelegate methods
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
// The fetch controller is about to start sending change notifications, so prepare the table view for updates.
[self.tableView beginUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
UITableView *tableView = self.tableView;
switch(type) {
case NSFetchedResultsChangeInsert:
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
[self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
// Reloading the section inserts a new row and ensures that titles are updated appropriately.
[tableView reloadSections:[NSIndexSet indexSetWithIndex:newIndexPath.section] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
switch(type) {
case NSFetchedResultsChangeInsert:
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
// The fetch controller has sent all current change notifications, so tell the table view to process all updates.
[self.tableView endUpdates];}
#end
The connections in the xib are>
table view
outlet:
data source-----------files owner;
delegate--------------files owner;
referencing outlets
tableView-------------files owner;
thank you so much!!!!
As for the "NSInternalConsistencyException" check the managed object model against your code and make sure you're calling it exactly right. Failing that, have you done a clean, restarted XCode, restarted your computer, deleted app from simulator, the usual stuff? If you've made changes to the file or swapped it out, the old stuff will sometimes "lurk" and cause these issues. Failing that, sometimes when I've had a confounding core data problem, rebuilding the model file from scratch and auto-creating the subclasses over will fix it...don't know why.
I know this wasn't your question, but it's widely discouraged to simply call the app delegate for the managed object context--though it does work and you'll see it in some code examples, it's not best practice. It introduces an unnecessary dependency in your project which will make your code harder to maintain. The app delegate should only be used to handle system-level events as it was designed, not "abused" for other purposes. The better way is to create a managed object context property for each controller that needs it, and pass the reference when that controller is created. Your App Delegate will pass it to the root view controller in didFinishLaunchingWithOptions (simply by setting the receiving controller's property), like this:
-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions...
{
UINavigationController *navController = self.window.rootViewController
MyViewController *controller = navController.viewControllers[0];
controller.managedObjectContext = self.managedObjectContext;
return YES;
}
When that root view controller needs to pass the MOC to another controller, it happens like this:
-(void)prepareForSegue...
{
MyOtherController *otherController = [segue destinationViewController];
controller.managedObjectContext = self.managedObjectContext;
}
And so on and so forth, for each controller that needs it. That way you aren't introducing unnecessary dependencies that make your code less modular and reusable.
Hope this helped.
Try to add this code to perform a fetch of data in your viewDidLoad method:NSError *error = nil;
if (![[self fetchedResultsController] performFetch:&error]) {
NSLog(#"Unsolved error: %#, %#", error, [error userInfo]);
abort();
}

Resources