I am thinking this is a bug in Core Data but before I file a bug report, I want to be sure it is not just me being stupid.
I set up an NSOutlineView to access the data of 5 different Core Data entities. Each entity's data is accessed with a different NSArrayController which is bound to the Entity and its' ManagedObjectContext. I then have the NSOutlineViewDataSource methods return the correct NSString object depending on which entity was expanded.
NOTE: entities is declared elsewhere as an NSArray with names for the entities.
- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index
ofItem:(id)item {
if(nil == item) {
return [entities objectAtIndex:index];
}
NSInteger entityIdx = [entities indexOfObject:item];
if (entityIdx == NSNotFound) {
return #"";
}
id returnObject = #"";
switch (entityIdx) {
case 0: {
Person *person = [[peopleArrayController arrangedObjects] objectAtIndex:index];
returnObject = person.fullName;
break;
}
case 1: {
Media *media = [[mediaArrayController arrangedObjects] objectAtIndex:index];
returnObject = media.imageTitle;
break;
}
case 2: {
Note *note = [[notesArrayController arrangedObjects] objectAtIndex:index];
returnObject = note.noteDescription;
break;
}
case 3: {
Source *source = [[sourcesArrayController arrangedObjects] objectAtIndex:index];
returnObject = source.title;
break;
}
case 4: {
Repository *repo = [[repostioriesArrayController arrangedObjects] objectAtIndex:index];
returnObject = repo.name;
break;
}
default:
break;
}
return returnObject;
}
The Person entity property fullName and the Media entity property imageTitle are custom accessors.
- (NSString *)fullName {
[self willAccessValueForKey:#"surName"];
[self willAccessValueForKey:#"givenName"];
NSString *firstName = [self valueForKey:#"givenName"];
NSString *lastName = [self valueForKey:#"surName"];
NSString *string = [NSString stringWithFormat:#"%# %#", (firstName) ? firstName : #"", (lastName) ? lastName : #""];
[self didAccessValueForKey:#"surName"];
[self didAccessValueForKey:#"givenName"];
return string;
}
- (id) imageTitle {
[self willAccessValueForKey:#"path"];
id title = [[self valueForKey:#"path"] lastPathComponent];
[self didAccessValueForKey:#"path"];
return title;
}
The program was crashing when I tried to expand the Person or the Media entities but not when I expanded the other entities. I traced the crash to [NSCell _setContents:][NSObject(NSObject) doesNotRecognizeSelector:]
I changed the Media property being returned to a standard Core Data accessor property #"path" and the program stopped crashing when I expanded the Media entity. So the problem is definitely related to the custom accessor.
FYI - I checked to make sure the entity was set to use the NSManagedObject class.
Can anyone give me a reason for the crash other than a bug?
I got around this problem by changing the method
- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item
to return the managed object
case 1: {
Media *media = [[mediaArrayController arrangedObjects] objectAtIndex:index];
returnObject = media;
break;
}
Then I have the method
- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
return the String
if ([item isKindOfClass:[Media class]]) {
Media *media = (Media *)item;
return media.imageTitle;
}
Related
I have a number of attributes for a CorData entity that are based on the values of other attributes. For example transactionTotalValue = transactionPrice * transactionQuantity. Currently I subclassed the NSManagedObject and created custom setters like this
- (void)setTransactionQuantity:(NSDecimalNumber *)transactionQuantity
{
[self willChangeValueForKey:#"transactionQuantity"];
[self setPrimitiveValue:transactionQuantity forKey:#"transactionQuantity"];
[self didChangeValueForKey:#"transactionQuantity"];
[self updateTotalValue];
}
- (void)setTransactionPrice:(NSDecimalNumber *)transactionPrice
{
[self willChangeValueForKey:#"transactionPrice"];
[self setPrimitiveValue:transactionPrice forKey:#"transactionPrice"];
[self didChangeValueForKey:#"transactionPrice"];
[self updateTotalValue];
}
- (void)updateTotalValue
{
self.transactionTotalValue = [self.transactionQuantity decimalNumberByMultiplyingBy:[NSDecimalNumber decimalNumberWithDecimal:[self.transactionPrice decimalValue]]];
}
Is this an acceptable way of doing this? if not what would be considered best practice for this situation?
The other alternative is to use KVO as follows
- (NSDecimalNumber *)transactionTotalValue
{
[self willAccessValueForKey:#"transactionTotalValue"];
NSDecimalNumber *total = [self.transactionQuantity decimalNumberByMultiplyingBy:[NSDecimalNumber decimalNumberWithDecimal:[self.transactionPrice decimalValue]]];
[self didChangeValueForKey:#"transactionTotalValue"];
return total;
}
+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key
{
NSSet *keypaths = [super keyPathsForValuesAffectingValueForKey:key];
if ([key isEqualToString:#"transactionTotalValue"]) {
NSArray *affectingKeys = #[#"transactionQuantity", #"transactionPrice"];
keypaths = [keypaths setByAddingObjectsFromArray:affectingKeys];
}
return keypaths;
}
Is this the better option?
The attribute transactionTotalValue is to be completely dependent upon the two other attributes.
Declare it as a readonly, nonatomic property.
#property (nonatomic,readonly) id transactionTotalValue;
You can then implement the getter.
- (id)transactionTotalValue
{
//check for non-existent needed properties and handle here
return [self.transactionQuantity
decimalNumberByMultiplyingBy:[NSDecimalNumber
decimalNumberWithDecimal:[self.transactionPrice decimalValue]]];
}
Also override the class method for keys that affect the dependent property.
+ (NSSet *)keyPathsForValuesAffectingTransactionTotalValue
{
return [NSSet setWithObjects:#"transactionQuantity", #"transactionPrice", nil];
}
The transactionTotalValue property will be re-read as needed, say if you are updating a table source through bindings. By making it readonly you will be able to avoid any setter methods.
I'm having an issue where when you select a top level object in an NSOutlineView, an error message is generated saying:
"View Based NSTableView error: preparedCellAtColumn:row: was called. Please log a bug with the backtrace from this log, or stop using the method."
The NSOutlineView I am using is set to View Based. I have no idea why the preparedCellAtColumn method is even being called. I added the method and placed a breakpoint to try and trace what is calling it, but XCode looks to be blocking the execution of it when it fires this exception.
Edit - Delegate and DateSource Methods
- (BOOL) itemAtIndexIsHeader: (NSInteger) index
{
return [self isHeader: [_projectPane itemAtRow: index]];
}
- (BOOL) isHeader: (id) item
{
return [item isKindOfClass: [Folder class]];
}
- (BOOL) outlineView:(NSOutlineView *)outlineView isGroupItem:(id)item
{
return NO;
}
- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item
{
//item is nil when the outline view wants to inquire for root level items
if (item == nil)
return [[[PMDataManager sharedManager] allFolders] objectAtIndex: index];
else{
Folder *folder = (Folder *) item;
return [[[folder projects] allObjects] objectAtIndex: index];
}
}
- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
{
return [self isHeader: item];
}
- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
{
if (item == nil) { //item is nil when the outline view wants to inquire for root level items
return [[[PMDataManager sharedManager] allFolders] count];
}
else if ([self isHeader: item]) {
Folder *folder = (Folder *) item;
return [[[folder projects] allObjects] count];
}
return 0;
}
- (NSView *)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(id)item {
if ([self isHeader: item]){
PMProjectHeaderCell *cell = [outlineView makeViewWithIdentifier:#"HeaderCell" owner:self];
Folder *folder = (Folder *) item;
[[cell headerText] setStringValue: [folder name]];
return cell;
}
else{
PMProjectCell *cell = [outlineView makeViewWithIdentifier:#"ProjectCell" owner:self];
Project *project = (Project *) item;
[[cell projectNameTextField] setStringValue: [project name]];
return cell;
}
return nil;
}
- (void) outlineViewSelectionDidChange:(NSNotification *)notification
{
selectedProjectIndex = [_projectPane selectedRow];
[self reloadRightPane];
[self refresh: nil];
}
Verify that your table view content mode matches the datasource/delegate methods you are trying to use.
If you're trying to use cell-based datasource, verify that the table view content mode is "Cell Based". The same for view based.
I've an ARC enabled project and within IB I've created a window that holds the source list component which I believe is just a configured NSOutlineView. I'm using the magical delegate method:
- (id)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(id)item
for which I cannot find any documentation for at all. Once this method is implemented the root node in my outline view will appear, upon which my entire model gets deallocated. Then when I try and expand the root node the app immediately crashes as model no longer exists.
If I don't use this method, my model remains, the source list works but none of the cells appear (understandably). I'm really not doing any thing fancy here at all.
I've never run into this sort of issue with ARC before, but it's late so there is a chance I've done something dumb and just can't see it. Here's the full code:
#implementation RLListController
- (void)awakeFromNib
{
RLPerson *stan = [[RLPerson alloc] initWithName:#"Stan"];
RLPerson *eric = [[RLPerson alloc] initWithName:#"Eric"];
RLPerson *ken = [[RLPerson alloc] initWithName:#"Ken"];
RLPerson *andrew = [[RLPerson alloc] initWithName:#"Andrew"];
RLPerson *daniel = [[RLPerson alloc] initWithName:#"Daniel"];
RLPerson *aksel = [[RLPerson alloc] initWithName:#"Aksel"];
[stan addChild:eric];
[stan addChild:ken];
[stan addChild:andrew];
[ken addChild:daniel];
[daniel addChild:aksel];
self.people = [#[stan] mutableCopy];
}
#pragma mark - Source List dataSource
- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
{
RLPerson *person = item;
return (item != nil) ? [person.children count] : [self.people count];
}
- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
{
RLPerson *person = item;
return (item != nil) ? [person.children count] > 0 : YES;
}
- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item
{
RLPerson *person = item;
return (item != nil) ? [person.children objectAtIndex:index] : [self.people objectAtIndex:index];
}
- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
{
RLPerson *person = item;
return person.name;
}
- (id)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(id)item
{
RLPerson *person = item;
NSTableCellView *cell = [outlineView makeViewWithIdentifier:#"DataCell" owner:self];
cell.objectValue = person;
[cell.textField setStringValue:person.name];
return cell;
}
#end
#implementation RLPerson
- (id)initWithName:(NSString *)name
{
self = [super init];
if(self)
{
_name = [name copy];
_children = [[NSMutableArray alloc] initWithCapacity:0];
}
return self;
}
- (void)addChild:(RLPerson *)child
{
[_children addObject:child];
}
- (void)dealloc
{
NSLog(#"dealloc");
}
#end
I've just figured out a similar crash in my code. I'll describe what the cause was for me... I'm pretty sure the same applies here, but I haven't tested your code.
Be aware that awakeFromNib can be called multiple times if you have multiple NIBs. I believe this is the case if you have NSTableCellView objects embedded within the NSOutlineView within your XIB file, which are extracted when you call makeViewWithIdentifier:owner: within outlineView:viewForTableColumn:item:.
Because you are creating your model objects (stan etc) within awakeFromNib, they are being recreated during these multiple calls. With each call, ARC is cleaning up the previous model objects, but NSOutlineView is still referencing them, hence the later crash when NSOutlineView tries to ask them for more information.
The fix is to move the model object creation out of awakeFromNib, perhaps into an init method instead.
Update:
Some other small points... it also took me a while to find the documentation for the magic outlineView:viewForTableColumn:item: method. For some reason, it is part of the NSOutlineViewDelegate protocol, not NSOutlineViewDataSource. I believe that if you implement this method, you don't need an implementation of outlineView:objectValueForTableColumn:byItem:.
I have a Master/Details Application.By default, we have the MasterViewController attached to a TableViewController, in addition, i have attached it to an Sqlite database and all the data are showing correctly as they should. So i added a UISearchBar, in order to search upon all the items ; Search functionality's were working fine but the only bug was, the search bar disappears when scrolling down. So as a solution, i removed the TableViewController and created a simple UIVIewController and added a TableView , TableViewCell and a search bar in order to keep the search bar fix on top of the View as suggested by many people.Now the difference between those two concepts is, the TableViewController (First Case) loads the whole data in the cells once the application loads, when the ViewController ( Second Case , with a tableView , tableViewCell and a searchBar) does not load anything at startup, It loads the different elements when the user start writing a word in the searchBar. How can i force the tableView to load all Elements at startup, just like if you have a TableViewCOntroller?
This is my code :
//
// MasterViewController.m
#import "MasterViewController.h"
#import "DetailViewController.h"
#import <sqlite3.h>
#import "Author.h"
#interface MasterViewController ()
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath;
#end
#implementation MasterViewController
NSString *authorNAme , *authorNAme2;
#synthesize tableView;
#synthesize searchWasActive;
#synthesize detailViewController = _detailViewController;
#synthesize fetchedResultsController = __fetchedResultsController;
#synthesize managedObjectContext = __managedObjectContext;
#synthesize theauthors;
NSString *PathDB;
-(BOOL)canBecomeFirstResponder{
return YES;
}
- (void)awakeFromNib
{
// self.clearsSelectionOnViewWillAppear = NO;
self.contentSizeForViewInPopover = CGSizeMake(320.0, 600.0);
[super awakeFromNib];
}
- (void)viewDidLoad
{
// [self numberOfSectionsInTableView:tableView];
//[self tableView:tableView willDisplayCell:[self.tableView dequeueReusableCellWithIdentifier:#"Cell"] forRowAtIndexPath:0];
//[self.tableView
searchBar.delegate = (id)self;
// [self.tableView reloadData];
// [self configureCell:#"Cell" atIndexPath:0];
[self authorList];
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.navigationItem.leftBarButtonItem = self.editButtonItem;
UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:#selector(insertNewObject:)];
self.navigationItem.rightBarButtonItem = addButton;
self.detailViewController = (DetailViewController *)[[self.splitViewController.viewControllers lastObject] topViewController];
}
- (void)viewDidUnload
{
[self setTableView:nil];
tableView = nil;
searchBar = nil;
[self setSearchWasActive:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
- (void)insertNewObject:(id)sender
{
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
NSEntityDescription *entity = [[self.fetchedResultsController fetchRequest] entity];
NSManagedObject *newManagedObject = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context];
// If appropriate, configure the new managed object.
// Normally you should use accessor methods, but using KVC here avoids the need to add a custom class to the template.
[newManagedObject setValue:[NSDate date] forKey:#"timeStamp"];
// Save the context.
NSError *error = nil;
if (![context 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.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
//[self authorList];
// [filteredTableData release];
//[self.tableView reloadData];
NSLog(#"Cancel Button Clicked");
// Scroll to top
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:YES];
self.searchDisplayController.searchBar.text =#" ";
}
- (void) searchBarTextDidBeginEditing:(UISearchBar *)sender
{
[self.tableView reloadData];
searchBar.showsCancelButton=NO;
}
-(void)searchBar:(UISearchBar*)searchBar textDidChange:(NSString*)text
{
searchBar.showsCancelButton=NO;
if(text.length == 0)
{
isFiltered = FALSE;
}
else
{
isFiltered = true;
filteredTableData = [[NSMutableArray alloc] init];
for (Author* author in theauthors)
{ //[NSPredicate predicateWithFormat:#"SELECT * from books where title LIKE %#", searchBar.text];
NSRange nameRange = [author.name rangeOfString:text options:NSAnchoredSearch];
NSRange descriptionRange = [author.genre rangeOfString:text options:NSAnchoredSearch];
if(nameRange.location != NSNotFound || descriptionRange.location != NSNotFound)
{
[filteredTableData addObject:author];
NSLog(#"Item Added is %#" , author.name);
}
}
}
[self.tableView reloadData];
}
#pragma mark - Table View methods
-(NSMutableArray *) authorList{
[self numberOfSectionsInTableView:tableView];
theauthors = [[NSMutableArray alloc] initWithCapacity:1000000];
// NSMutableArray * new2 = [[NSMutableArray alloc ] initWithCapacity:100000];
// authorNAme = theauthors.sortedArrayHint.description;
#try {
NSFileManager *fileMgr = [NSFileManager defaultManager];
NSLog(#"Before the dbpath variable");
NSString *dbPath = [[[NSBundle mainBundle] resourcePath ]stringByAppendingPathComponent:#"dictionary_native.sqlite"];
NSLog(#"After the dbpath variable");
BOOL success = [fileMgr fileExistsAtPath:dbPath];
if(!success)
{
NSLog(#"1");
NSLog(#"Cannot locate database file '%#'.", dbPath);
}
NSLog(#"Database correctly located");
if(!(sqlite3_open([dbPath UTF8String], &db) == SQLITE_OK))
{
NSLog(#"2");
NSLog(#"An error has occured: %#", sqlite3_errmsg(db));
}
NSLog(#"Database correctly opened");
// const char *sql = "SELECT F_Keyword FROM wordss";
const char *sql = "SELECT * FROM wordss";
sqlite3_stmt *sqlStatement;
if(sqlite3_prepare(db, sql, -1, &sqlStatement, NULL) != SQLITE_OK)
{
NSLog(#"Problem with prepare statement: %#", sqlite3_errmsg(db));
}else{
while (sqlite3_step(sqlStatement)==SQLITE_ROW) {
// NSLog(#"entered the while statement");
Author * author = [[Author alloc] init];
// // NSLog(#"Author initialised");
//
author.name = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement,1)];
// NSLog(#"this is the author.name %#" , author.name);
author.genre = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement, 2)];
//
// authorNAme=author.genre;
//
[theauthors addObject:author];
}
// authorNAme = author.genre;
}
}
#catch (NSException *exception) {
NSLog(#"Problem with prepare statement: %#", sqlite3_errmsg(db));
}
#finally {
// sqlite3_finalize(sqlStatement);.
// authorNAme = nil;
// authorNAme = Nil;
return theauthors;
}
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// return [[self.fetchedResultsController sections] count];
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
// return [sectionInfo numberOfObjects];
int rowCount;
if(self->isFiltered)
rowCount = filteredTableData.count;
else
rowCount = theauthors.count;
NSLog(#"This is the number of rows accepted %i" , rowCount);
return rowCount;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:#"Cell"];
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
int rowCount = indexPath.row;
Author *author = [self.theauthors objectAtIndex:rowCount];
if(isFiltered){
author = [filteredTableData objectAtIndex:indexPath.row];
}
else{
author = [theauthors objectAtIndex:indexPath.row];
}
cell.textLabel.text = author.name;
return cell;
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the specified item to be editable.
return YES;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
[context deleteObject:[self.fetchedResultsController objectAtIndexPath:indexPath]];
NSError *error = nil;
if (![context 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.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
}
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
// The table view should not be re-orderable.
return NO;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// NSManagedObject *object = [[self fetchedResultsController] objectAtIndexPath:indexPath];
// self.detailViewController.detailItem = object;
// DetailViewController* vc ;
MasterViewController *author;
NSLog(#"This is the showDetailsForIndexPath");
[self->searchBar resignFirstResponder];
// Details* vc = [self.storyboard instantiateViewControllerWithIdentifier:#"Details"];
// AuthorVC* author;
if(isFiltered)
{
author = [filteredTableData objectAtIndex:indexPath.row];
}
else
{
author = [theauthors objectAtIndex:indexPath.row];
}
//vc.author = author;
authorNAme = author.genre;
authorNAme2 = author.name ;
// author = [theauthors objectAtIndex:indexPath.row];
NSLog(#"This is the author.genre %#" , author.genre);
//vc.author.genre = author.genre;
authorNAme = author.genre;
authorNAme2 = author.name;
//NSLog(#"This is the details %#",vc.author.genre);
NSLog(#"This is the authorNAme Variable %#" , authorNAme);
self.detailViewController.detailItem = authorNAme;
self.detailViewController.detailItem2 = authorNAme2;
}
#pragma mark - Fetched results controller
- (NSFetchedResultsController *)fetchedResultsController
{
if (__fetchedResultsController != nil) {
return __fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Event" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:20];
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"timeStamp" ascending:NO];
NSArray *sortDescriptors = [NSArray arrayWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:#"Master"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&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.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return __fetchedResultsController;
}
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
[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:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (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];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
[self.tableView endUpdates];
}
/*
// Implementing the above methods to update the table view in response to individual changes may have performance implications if a large number of changes are made simultaneously. If this proves to be an issue, you can instead just implement controllerDidChangeContent: which notifies the delegate that all section and object changes have been processed.
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
// In the simplest, most efficient, case, reload the table view.
[self.tableView reloadData];
}
*/
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
// NSManagedObject *object = [self.fetchedResultsController objectAtIndexPath:indexPath];
// cell.textLabel.text = [[object valueForKey:#"timeStamp"] description];
}
#end
Any help Will be highly appreciated...Thank you for taking the time.
If the tableview is in xib link it to correct object and set delegate and datasource.
I a trying to build an application where i can use UICollectionviewcontroller & NSFetchresultcontroller , i found the following link "https://github.com/AshFurrow/UICollectionView-NSFetchedResultsController/blob/master/AFMasterViewController.m
Here you can find the Code
#pragma mark - UICollectionVIew
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return [[self.fetchedResultsController sections] count];
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
id <NSFetchedResultsSectionInfo> sectionInfo = [self.fetchedResultsController sections][section];
return [sectionInfo numberOfObjects];
}
// The cell that is returned must be retrieved from a call to -dequeueReusableCellWithReuseIdentifier:forIndexPath:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
AFCollectionViewCell *cell = (AFCollectionViewCell *)[collectionView dequeueReusableCellWithReuseIdentifier:CellIdentifier forIndexPath:indexPath];
NSManagedObject *object = [self.fetchedResultsController objectAtIndexPath:indexPath];
#warning Unimplement Cell Configuration
return cell;
}
#pragma mark - Fetched results controller
- (NSFetchedResultsController *)fetchedResultsController
{
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
#warning Unimplemented fetched results controller
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:<#entity#>inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:<#batch size#>];
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:<#descriptor#> ascending:NO];
NSArray *sortDescriptors = #[sortDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:#"Master"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&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.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return _fetchedResultsController;
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
{
NSMutableDictionary *change = [NSMutableDictionary new];
switch(type) {
case NSFetchedResultsChangeInsert:
change[#(type)] = #[#(sectionIndex)];
break;
case NSFetchedResultsChangeDelete:
change[#(type)] = #[#(sectionIndex)];
break;
}
[_sectionChanges addObject:change];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath
{
NSMutableDictionary *change = [NSMutableDictionary new];
switch(type)
{
case NSFetchedResultsChangeInsert:
change[#(type)] = newIndexPath;
break;
case NSFetchedResultsChangeDelete:
change[#(type)] = indexPath;
break;
case NSFetchedResultsChangeUpdate:
change[#(type)] = indexPath;
break;
case NSFetchedResultsChangeMove:
change[#(type)] = #[indexPath, newIndexPath];
break;
}
[_objectChanges addObject:change];
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
if ([_sectionChanges count] > 0)
{
[self.collectionView performBatchUpdates:^{
for (NSDictionary *change in _sectionChanges)
{
[change enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, id obj, BOOL *stop) {
NSFetchedResultsChangeType type = [key unsignedIntegerValue];
switch (type)
{
case NSFetchedResultsChangeInsert:
[self.collectionView insertSections:[NSIndexSet indexSetWithIndex:[obj unsignedIntegerValue]]];
break;
case NSFetchedResultsChangeDelete:
[self.collectionView deleteSections:[NSIndexSet indexSetWithIndex:[obj unsignedIntegerValue]]];
break;
case NSFetchedResultsChangeUpdate:
[self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:[obj unsignedIntegerValue]]];
break;
}
}];
}
} completion:nil];
}
if ([_objectChanges count] > 0 && [_sectionChanges count] == 0)
{
[self.collectionView performBatchUpdates:^{
for (NSDictionary *change in _objectChanges)
{
[change enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, id obj, BOOL *stop) {
NSFetchedResultsChangeType type = [key unsignedIntegerValue];
switch (type)
{
case NSFetchedResultsChangeInsert:
[self.collectionView insertItemsAtIndexPaths:#[obj]];
break;
case NSFetchedResultsChangeDelete:
[self.collectionView deleteItemsAtIndexPaths:#[obj]];
break;
case NSFetchedResultsChangeUpdate:
[self.collectionView reloadItemsAtIndexPaths:#[obj]];
break;
case NSFetchedResultsChangeMove:
[self.collectionView moveItemAtIndexPath:obj[0] toIndexPath:obj[1]];
break;
}
}];
}
} completion:nil];
}
[_sectionChanges removeAllObjects];
[_objectChanges removeAllObjects];
}
#end
I have the Following error
NSFetchedResultsController during a call to -controllerDidChangeContent:. -[__NSArrayI unsignedIntegerValue]
The NS Log for the _sectionsChange give me the following output
1 = (
6
);
I am trying to know how to solve this problem , so any help would be appreciated
Thank you
NO , the Error was fixed after i updated the following code:
{
case NSFetchedResultsChangeInsert:
[self.collectionView insertItemsAtIndexPaths:#[obj]];
[self.collectionView.window endEditing:YES];
break;
}
Instead of case NSFetchedResultsChangeInsert:
[self.collectionView insertItemsAtIndexPaths:#[obj]];
break;
Then delete all objects , create everything from scratch , problem solved !