not sure why NSFetchedResultsController isn't showing sections - iphone

I previously had a TableView which would get its 2 header views (and separator views) from an array called holdViewsArray in viewDidLoad, like so:
-(void) viewDidLoad{
// ---Start Core Data With NSFetchedResultsController---
[super viewDidLoad];
NSError *error;
if (![[self fetchedResultsController] performFetch:&error]){
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
exit(-1);
}
// ---End Core Data w/ NSFetchedResultsController---
[self.tableView setDelegate:self];
[self setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
holdViewsArray = [[NSMutableArray alloc]init];
UIView *seperatorView;
UIView *seperatorView2;
NSString *sectionTitle = #"Tasks To Complete";
NSString *section2Title = #"Completed Tasks";
UILabel *label = [[UILabel alloc]init];
UILabel *label2 = [[UILabel alloc]init];
label.frame = CGRectMake(10.0, 5.0, 320.0, 50.0);
label.text = sectionTitle;
label2.frame = CGRectMake(10.0, 0.0, 320.0, 40.0);
label2.text = section2Title;
headerView = [[UIView alloc]initWithFrame:label.frame];
headerView2 = [[UIView alloc]initWithFrame:label2.frame];
CGRect sepFrame = CGRectMake(0, headerView.frame.size.height-2, 320, 1);
CGRect sep2Frame =CGRectMake(0, headerView2.frame.size.height-2, 320, 1);
seperatorView = [[UIView alloc] initWithFrame:sepFrame];
seperatorView2 = [[UIView alloc]initWithFrame:sep2Frame];
[headerView addSubview:seperatorView];
[headerView2 addSubview:seperatorView2];
[headerView addSubview:label];
[headerView addSubview:button];
[headerView2 addSubview:label2];
[holdViewsArray addObject:headerView];
[holdViewsArray addObject:headerView2];
}
After implementing NSFetchedResultsController, I decided that I wanted to use NSFetchedResultsController's properties and methods to make the header views and split the table view into 2 sections. I've tried to do so, but its not giving me any titles in my sections and it just looks awful. How would I keep the same look of the headers that I have now but use NSFetchedResultsController to split the table view into 2 sections (complete and incomplete tasks)? Here is what I tried:
Tasks Core Data property (optional property):
#interface Tasks : NSManagedObject
#property (nonatomic, retain) NSString *sectionString;
#end
#implementation Tasks
#dynamic sectionString;
#end
Adding two tasks in different sections in the App Delegate
NSManagedObjectContext *context = self.managedObjectContext;
NSManagedObject *startingTask = [NSEntityDescription insertNewObjectForEntityForName:#"Tasks" inManagedObjectContext:context];
[startingTask setValue:#"Eat Dinner" forKey:#"taskName"];
[startingTask setValue:[NSNumber numberWithDouble:400] forKey:#"timeInterval"];
[startingTask setValue:#"Tasks To Complete" forKey:#"sectionString"];
NSManagedObject *finishedTask = [NSEntityDescription insertNewObjectForEntityForName:#"Tasks" inManagedObjectContext:context];
[finishedTask setValue:#"Do Laundry" forKey:#"taskName"];
[finishedTask setValue:[NSNumber numberWithDouble:400] forKey:#"timeInterval"];
[finishedTask setValue:#"Completed Tasks" forKey:#"sectionString"];
NSError *error;
if (![context save:&error]) {
NSLog(#"couldn't save: %#", [error localizedDescription]);
}
Splitting into two sections
- (NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Tasks" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *isCompleted = [[NSSortDescriptor alloc]initWithKey:#"sectionString" ascending:NO];
NSSortDescriptor *sort = [[NSSortDescriptor alloc]
initWithKey:#"dateCreated" ascending:NO];
[fetchRequest setSortDescriptors:#[isCompleted, sort]];
[fetchRequest setFetchBatchSize:20];
NSFetchedResultsController *theFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:managedObjectContext sectionNameKeyPath:#"sectionString"
cacheName:#"Root"];
self.fetchedResultsController = theFetchedResultsController;
_fetchedResultsController.delegate = self;
return _fetchedResultsController;
}
What am I doing wrong that's causing it to not split into 2 sections with titles of "Tasks To Complete" and "Completed Tasks"?

Related

Weird Header Error when using NSFetchedResultsController

Since I can't really describe this error, I recorded my screen with the error.
http://www.youtube.com/watch?v=w2FqKKcL2Ck&feature=youtu.be
Basically I am not sure what to do. When the user finishes all the tasks in the Tasks To Complete section, I would like the section header to remain there...just with no objects in it (simply because its needed in order to add more tasks).
Luckily when the user doesn't have any Completed Tasks, the header there disappears which is what should happen. I would like the top header to not disappear though...
Also, same thing with the delete. I don't really want the Tasks To Complete section to disappear completely when the last object is deleted.
I've heard this is not possible unless you use a custom subclass like TAFetchResultsController, but I tried using that and it was just too complex for me to implement (and kind of broke my application rather than fix it). Maybe you guys have some suggestions?
Here is some relevant code:
Tasks core data properties
#interface Tasks : NSManagedObject
#property (nonatomic, retain) NSString *sectionString;
#end
#implementation Tasks
#dynamic sectionString;
#end
adding tasks to different sections
NSManagedObjectContext *context = self.managedObjectContext;
NSManagedObject *startingTask = [NSEntityDescription insertNewObjectForEntityForName:#"Tasks" inManagedObjectContext:context];
[startingTask setValue:#"Eat Dinner" forKey:#"taskName"];
[startingTask setValue:[NSNumber numberWithDouble:400] forKey:#"timeInterval"];
[startingTask setValue:#"Tasks To Complete" forKey:#"sectionString"];
NSManagedObject *finishedTask = [NSEntityDescription insertNewObjectForEntityForName:#"Tasks" inManagedObjectContext:context];
[finishedTask setValue:#"Do Laundry" forKey:#"taskName"];
[finishedTask setValue:[NSNumber numberWithDouble:400] forKey:#"timeInterval"];
[finishedTask setValue:#"Completed Tasks" forKey:#"sectionString"];
NSError *error;
if (![context save:&error]) {
NSLog(#"couldn't save: %#", [error localizedDescription]);
}
TableViewController.m:
-(void) viewDidLoad{
// ---Start Core Data With NSFetchedResultsController---
[super viewDidLoad];
NSError *error;
if (![[self fetchedResultsController] performFetch:&error]){
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
exit(-1);
}
// ---End Core Data w/ NSFetchedResultsController---
[self.tableView setDelegate:self];
[self setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
holdViewsArray = [[NSMutableArray alloc]init];
UIView *seperatorView;
UIView *seperatorView2;
NSString *sectionTitle = #"Tasks To Complete";
NSString *section2Title = #"Completed Tasks";
UILabel *label = [[UILabel alloc]init];
UILabel *label2 = [[UILabel alloc]init];
label.frame = CGRectMake(10.0, 5.0, 320.0, 50.0);
label.text = sectionTitle;
label2.frame = CGRectMake(10.0, 0.0, 320.0, 40.0);
label2.text = section2Title;
headerView = [[UIView alloc]initWithFrame:label.frame];
headerView2 = [[UIView alloc]initWithFrame:label2.frame];
CGRect sepFrame = CGRectMake(0, headerView.frame.size.height-2, 320, 1);
CGRect sep2Frame =CGRectMake(0, headerView2.frame.size.height-2, 320, 1);
seperatorView = [[UIView alloc] initWithFrame:sepFrame];
seperatorView2 = [[UIView alloc]initWithFrame:sep2Frame];
[headerView addSubview:seperatorView];
[headerView2 addSubview:seperatorView2];
[headerView addSubview:label];
[headerView addSubview:button];
[headerView2 addSubview:label2];
[holdViewsArray addObject:headerView];
[holdViewsArray addObject:headerView2];
- (NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Tasks" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *isCompleted = [[NSSortDescriptor alloc]initWithKey:#"sectionString" ascending:NO];
NSSortDescriptor *sort = [[NSSortDescriptor alloc]
initWithKey:#"dateCreated" ascending:YES];
[fetchRequest setSortDescriptors:#[isCompleted, sort]];
[fetchRequest setFetchBatchSize:20];
NSFetchedResultsController *theFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:managedObjectContext sectionNameKeyPath:#"sectionString"
cacheName:nil];
self.fetchedResultsController = theFetchedResultsController;
_fetchedResultsController.delegate = self;
return _fetchedResultsController;
}-(void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
Tasks *task = [_fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = task.taskName.uppercaseString;
cell.detailTextLabel.text = [NSString stringWithFormat:#"%.0f", task.timeInterval];
cell.imageView.image = [UIImage imageNamed:#"unchecked.png"];
cell.imageView.highlightedImage = [UIImage imageNamed:#"uncheckedhighlighted.png"];
[cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator];
if (indexPath.section == 1)
[cell.contentView setAlpha:0.5];
else {
[cell.contentView setAlpha:1];
}
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(handlechecking:)];
[cell.imageView addGestureRecognizer:tap];
cell.imageView.userInteractionEnabled = YES;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
cellSubclassCell *cell = [tableView dequeueReusableCellWithIdentifier:#"UITableViewCell"];
if (!cell)
cell = [[cellSubclassCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"UITableViewCell"];
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
-(void)handlechecking:(UITapGestureRecognizer *)t{
CGPoint tapLocation = [t locationInView:self.tableView];
NSIndexPath *tappedIndexPath = [self.tableView indexPathForRowAtPoint:tapLocation];
Tasks *task = [_fetchedResultsController objectAtIndexPath:tappedIndexPath];
if ([task.sectionString isEqual: #"Tasks To Complete"]){
task.sectionString = #"Completed Tasks";
} else if ([task.sectionString isEqualToString:#"Completed Tasks"]){
task.sectionString = #"Tasks To Complete";
}
[self.tableView reloadData];
NSTimeInterval time = [[NSDate date] timeIntervalSinceReferenceDate];
[task setDateCreated:time];
}
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{
switch (section) {
case 0:
return [holdViewsArray objectAtIndex:0];
break;
case 1:
return [holdViewsArray objectAtIndex:1];
break;
}
return 0;
}
NSFetchedResultsController infers sections from the objects returned by the fetch. So if the number of items a section goes to zero, the section vanishes. That's just how NSFetchedResultsController works.
Your simplest option is probably a 3rd-party framework. Take a look at TLIndexPathTools. It provides an alternative to NSFetchedResultsController that does not require Core Data or an NSFetchRequest, meaning you can use it in more scenarios. Take a look at some of the example projects (the Core Data one is called "Core Data")- some cool things are done with few lines of code.
TLIndexPathTools supports empty sections, but not explicitly in the way you need. However, you can accomplish it with a few lines of code by overriding TLIndexPathController. The code would look something like this:
#interface MyIndexPathController : TLIndexPathController
#end
#import "MyIndexPathController.h"
#implementation MyIndexPathController
- (void)setDataModel:(TLIndexPathDataModel *)dataModel
{
if ([dataModel sectionForSectionName:#"MyFirstSectionName"] == NSNotFound) {
TLIndexPathSectionInfo *firstSection = [[TLIndexPathSectionInfo alloc] initWithItems:nil andName:#"MyFirstSectionName"];
NSMutableArray *sectionInfos = [NSMutableArray arrayWithArray:dataModel.sections];
[sectionInfos insertObject:firstSection atIndex:0];
dataModel = [[TLIndexPathDataModel alloc] initWithSectionInfos:sectionInfos andIdentifierKeyPath:dataModel.identifierKeyPath andCellIdentifierKeyPath:dataModel.cellIdentifierKeyPath];
}
super.dataModel = dataModel;
}
#end
What is happening here is we intercept the data model generated by the controller (based on the results of our fetch request) and check if the section of interest is present. If not, we create an empty section info object and combine it with the other section infos into a new data model and pass it along to the super class.
It should not be too difficult to extend generically to work with any set of required sections if needed.

NSFetchedResultsController returning objects with another context

In order to get information from an entity I have called Entry for my table view cells, i have this code:
Entry *entry = [self.appDelegate.fetchedResultsController objectAtIndexPath:indexPath];
NSLog(#"timeline entry contexts: %# and %#", [[AppDelegate applicationDelegate] managedObjectContext], [entry managedObjectContext]);
The two managedObjectContexts return as being different. Here is my fetched results controller:
- (NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Entry" inManagedObjectContext:[AppDelegate applicationDelegate].managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setFetchBatchSize:20];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"creationDate" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:[AppDelegate applicationDelegate].managedObjectContext sectionNameKeyPath:#"sectionIdentifier" cacheName:#"Root"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
self.fetchedResultsController.delegate = self;
return _fetchedResultsController;
}
And here's my MOC:
- (NSManagedObjectContext *)managedObjectContext {
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
NSManagedObjectContext* moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[moc performBlockAndWait:^{
[moc setPersistentStoreCoordinator: coordinator];
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(mergeChangesFrom_iCloud:) name:NSPersistentStoreDidImportUbiquitousContentChangesNotification object:coordinator];
}];
_managedObjectContext = moc;
_managedObjectContext.mergePolicy = [[NSMergePolicy alloc]
initWithMergeType:NSMergeByPropertyObjectTrumpMergePolicyType];
}
return _managedObjectContext;
}
I'm unsure why it's returning a different MOC. Any thoughts?
You could try clearing the cache Root before executing the request declaring the root
[NSFetchedResultsController deleteCacheWithName:#"Root"];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:[AppDelegate applicationDelegate].managedObjectContext sectionNameKeyPath:#"sectionIdentifier" cacheName:#"Root"];
I came across something similar lately and that fixed the problem

Using Search bar with Core Data

I have tried various methods and this is the closest I have gotten, but the updating of the table makes all the entries off screen blank, and when I scroll, they all blank out. I am still new and am not entirely sure about implementing a search on core data tableview at all.
If I don't nil things out it dies here:
UPDATE: this is what I am trying now, and I am getting the same crash:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'no section at index 1'
`Recipe *recipe = (Recipe *)[fetchedResultsController objectAtIndexPath:indexPath];` **`Thread 1 received signal SIGABRT`**
#pragma mark -
#pragma mark search bar methods
- (void) searchBarSearchButtonClicked:(UISearchBar *)searchBar {
NSLog(#"searched");
fetchedResultsController = nil;
//============
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Recipe" inManagedObjectContext:managedObjectContext];
if (searchBar.text !=nil) {
NSPredicate *predicate =[NSPredicate predicateWithFormat:#"name contains[cd] %#", searchBar.text];
[fetchedResultsController.fetchRequest setPredicate:predicate];
} else {
NSPredicate *predicate =[NSPredicate predicateWithFormat:#"All"];
[fetchedResultsController.fetchRequest setPredicate:predicate];
}
[fetchRequest setEntity:entity];
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"state" ascending:YES];// was name
NSSortDescriptor *sortDescriptor2 = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor,sortDescriptor2, nil];// was 2
[fetchRequest setSortDescriptors:sortDescriptors];
// Edit the section name key path and cache name if appropriate.
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:#"state" cacheName:#"Root"];//#"state"
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
[aFetchedResultsController release];
[fetchRequest release];
[sortDescriptor release];
[sortDescriptor2 release];
[sortDescriptors release];
//==============
// dismiss the search keyboard
[searchBar resignFirstResponder];
// reload the table view
//[self.tableView reloadData];
}
- (void)configureCell:(RecipeTableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
// Configure the cell //DIES HERE
Recipe *recipe = (Recipe *)[fetchedResultsController objectAtIndexPath:indexPath];
cell.recipe = recipe;
}
In other conditions, it dies when I try to move the table up or down.
Thanks for any guidance or help with understanding and solving this issue!
Rob
tableViewController.m
- (NSFetchedResultsController *)fetchedResultsController {
if (fetchedResultsController == nil) {
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Recipe" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"state" ascending:YES];// was name
NSSortDescriptor *sortDescriptor2 = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor,sortDescriptor2, nil];// was 2
[fetchRequest setSortDescriptors:sortDescriptors];
// Edit the section name key path and cache name if appropriate.
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:#"state" cacheName:#"Root"];//#"state"
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
[aFetchedResultsController release];
[fetchRequest release];
[sortDescriptor release];
[sortDescriptor2 release];
[sortDescriptors release];
}
return fetchedResultsController;
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
self.fetchedResultsController = nil;
self.searchBar.text=#"";
[self.searchBar setShowsCancelButton:NO animated:YES];
[self.searchBar resignFirstResponder];
//self.tableView.allowsSelection = YES;
//self.tableView.scrollEnabled = YES;
}
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
// added 2 below
//self.fetchedResultsController.delegate = nil;
self.fetchedResultsController = nil;
[self.searchBar setShowsCancelButton:YES animated:YES];
//self.tableView.allowsSelection = NO;
//self.tableView.scrollEnabled = NO;
}
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
// added 2 below
//self.fetchedResultsController.delegate = nil;
self.fetchedResultsController = nil;
NSLog(#"fetchObjects");
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Recipe" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
// Edit the sort key as appropriate.
//NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"state" ascending:YES];// was name
NSSortDescriptor *sortDescriptor2 = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES];
//NSLog(#"NSInteger value :%#", sortDescriptor);
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor2, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// perform query
NSString *query = self.searchBar.text;
if (query && query.length) fetchRequest.predicate = [NSPredicate predicateWithFormat:#"name CONTAINS[cd] %#", query];
// Edit the section name key path and cache name if appropriate.
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:#"state" cacheName:#"Root"];//#"state"
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
[aFetchedResultsController release];
[fetchRequest release];
//[sortDescriptor release];
[sortDescriptor2 release];
[sortDescriptors release];
//[self.tableView reloadData];
}
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
// The fetch controller is about to start sending change notifications, so prepare the table view for updates.
[self.tableView beginUpdates];
}
It doesn't look like you don't ever actually perform the fetch request. From the docs:
After creating an instance, you invoke performFetch: to actually
execute the fetch.
Are you using a UISearchDisplayController? if you are you end up with two tables: one that displays your normal stuff and the other related to the search. You have to treat the tables separately.
There is a writeup of all the code you need to implement searching on tables at How to filter NSFetchedResultsController (CoreData) with UISearchDisplayController/UISearchBar

Core data app is crashing while perform fetch request

I am fetching result from core data entity
- (NSFetchedResultsController *)fetchedResultsController {
if (fetchedResultsController != nil) {
return fetchedResultsController;
}
// Set up the fetched results controller.
// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Tutorial" inManagedObjectContext: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:#"id" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
//sorting
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:#"Root"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
//release here
[aFetchedResultsController release];
[fetchRequest release];
[sortDescriptor release];
[sortDescriptors release];
return fetchedResultsController;
}
My app is crashing while perform fetch
- (void)viewDidLoad
{
[super viewDidLoad];
//title
self.navigationItem.leftBarButtonItem = self.editButtonItem;
//add song button
UIBarButtonItem *addSongbutton = [[UIBarButtonItem alloc] initWithTitle:#"Add Songs" style:UIBarButtonItemStyleBordered target:self action:#selector(addSongs)];
self.navigationItem.rightBarButtonItem = addSongbutton;
[addSongbutton release];
if (managedObjectContext == nil) {
managedObjectContext = [(SongsWithLyricsAppDelegate *)[[UIApplication sharedApplication]delegate]managedObjectContext];
}
self.title = #"Song List";
//Error message
NSError *error =nil;
if (![[self fetchedResultController]performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
this is Nslog output
Unresolved error (null), (null)
please help
grrr ... always push return in a comment ... I wanted to add that an error output of (null) (null) looks to me as if there is no error, but then the fetch shouldn't return false.
Are you sure fetchedResultsController is not nil when you try to call performFetch on it?
Might it be that your implementation of setFetchedResultsController has a bug?

tableView:didSelectRowAtIndexPath: calls TTNavigator openURLAction:applyAnimated: — UITabBar and navigationItem disappear

I have an existing iphone project with a UITabBar. Now I need styled text and in-text links to other ViewControllers in my app. I am trying to integrate TTStyledTextLabel.
I have a FirstViewController:UITabelViewController with this tableView:didSelectRowAtIndexPath:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *url;
if ([self.filteredQuestions count]>0) {
url = [NSString stringWithFormat:#"%#%d", #"tt://question/", [[self.filteredQuestions objectAtIndex:indexPath.row] id]];
[[TTNavigator navigator] openURLAction:[[TTURLAction actionWithURLPath: url] applyAnimated:YES]];
} else {
Question * q = [self.questions objectAtIndex:indexPath.row] ;
url = [NSString stringWithFormat:#"%#%d", #"tt://question/", [q.id intValue]];
}
TTDPRINT(#"%#", url);
TTNavigator *navigator = [TTNavigator navigator];
[navigator openURLAction:[[TTURLAction actionWithURLPath: url] applyAnimated:YES]];
}
My mapping looks like this:
TTNavigator* navigator = [TTNavigator navigator];
navigator.window = window;
navigator.supportsShakeToReload = YES;
TTURLMap* map = navigator.URLMap;
[map from:#"*" toViewController:[TTWebController class]];
[map from:#"tt://question/(initWithQID:)" toViewController:[QuestionDetailViewController class]];
and my QuestionDetailViewController:
#interface QuestionDetailViewController : UIViewController <UIScrollViewDelegate , QuestionDetailViewProtocol> {
Question *question;
}
#property(nonatomic,retain) Question *question;
-(id) initWithQID:(NSString *)qid;
-(void) goBack:(id)sender;
#end
When I hit a cell, q QuestionDetailViewController will be called — but the navigationBar wont
#implementation QuestionDetailViewController
#synthesize question;
-(id) initWithQID:(NSString *)qid
{
if (self = [super initWithNibName:#"QuestionDetailViewController" bundle:nil]) {
//;
TTDPRINT(#"%#", qid);
NSManagedObjectContext *managedObjectContext = [(domainAppDelegate*)[[UIApplication sharedApplication] delegate] managedObjectContext];
NSPredicate *predicate =[NSPredicate predicateWithFormat:#"id == %#", qid];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Question"
inManagedObjectContext:managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entity];
[request setPredicate:predicate];
NSError *error = nil;
NSArray *array = [managedObjectContext executeFetchRequest:request error:&error];
if (error==nil && [array count]>0 ) {
self.question = [array objectAtIndex:0];
} else {
TTDPRINT(#"error: %#", array);
}
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
[TTStyleSheet setGlobalStyleSheet:[[[TextTestStyleSheet alloc] init] autorelease]];
[self.navigationController.navigationBar setTranslucent:YES];
NSArray *includedLinks = [self.question.answer.text vs_extractExternalLinks];
NSMutableDictionary *linksToTT = [[NSMutableDictionary alloc] init];
for (NSArray *a in includedLinks) {
NSString *s = [a objectAtIndex:3];
if ([s hasPrefix:#"/answer/"] || [s hasPrefix:#"http://domain.com/answer/"] || [s hasPrefix:#"http://www.domain.com/answer/"]) {
NSString *ttAdress = #"tt://question/";
NSArray *adressComps = [s pathComponents];
for (NSString *s in adressComps) {
if ([s isEqualToString:#"qid"]) {
ttAdress = [ttAdress stringByAppendingString:[adressComps objectAtIndex:[adressComps indexOfObject:s]+1]];
}
}
[linksToTT setObject:ttAdress forKey:s];
}
}
for (NSString *k in [linksToTT allKeys]) {
self.question.answer.text = [self.question.answer.text stringByReplacingOccurrencesOfString:k withString: [linksToTT objectForKey:k]];
}
TTStyledTextLabel *answerText = [[[TTStyledTextLabel alloc] initWithFrame:CGRectMake(0, 0, 320, 700)] autorelease];
if (![[self.question.answer.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] hasPrefix:#"<div"]) {
self.question.answer.text = [NSString stringWithFormat:#"%<div>%#</div>", self.question.answer.text];
}
NSString * s = [NSString stringWithFormat:#"<div class=\"header\">%#</div>\n%#",self.question.title ,self.question.answer.text];
answerText.text = [TTStyledText textFromXHTML:s lineBreaks:YES URLs:YES];
answerText.contentInset = UIEdgeInsetsMake(20, 15, 20, 15);
[answerText sizeToFit];
[self.navigationController setNavigationBarHidden:NO animated:YES];
[self.view addSubview:answerText];
[(UIScrollView *)self.view setContentSize:answerText.frame.size];
self.view.backgroundColor = [UIColor whiteColor];
[linksToTT release];
}
.......
#end
This works quite nice, as soon as a cell is touched, a QuestionDetailViewController is called and pushed — but the tabBar will disappear, and the navigationItem — I set it like this: self.navigationItem.title =#"back to first screen"; — won't be shown. And it just appears without animation.
But if I press a link inside the TTStyledTextLabel the animation works, the navigationItem will be shown.
How can I make the animation, the navigationItem and the tabBar be shown?
I found a solution:
My QuestionDetailViewController implements the TTNavigatorDelegate.
-(BOOL)navigator:(TTNavigator *)navigator shouldOpenURL:(NSURL *)URL will always return NO, but will call [self.navigationController pushViewController:c animated:YES];
-(BOOL)navigator:(TTNavigator *)navigator shouldOpenURL:(NSURL *)URL {
NSEntityDescription *entity;
NSPredicate *predicate;
NSFetchRequest *request = [[NSFetchRequest alloc] init];
if ([[URL host] isEqualToString:#"question"]) {
entity =[NSEntityDescription entityForName:#"Question" inManagedObjectContext:managedObjectContext];
predicate = [NSPredicate predicateWithFormat:#"id == %#", [[URL path] stringByReplacingOccurrencesOfString:#"/" withString:#""]];
[request setEntity:entity];
[request setPredicate:predicate];
NSError *error =nil;
NSArray *array = [managedObjectContext executeFetchRequest:request error:&error];
if (error==nil && [array count] >0) {
QuestionDetailViewController *c = [[[QuestionDetailViewController alloc] init] autorelease];
c.question = [array objectAtIndex:0];
[self.navigationController pushViewController:c animated:YES];
}
}
[request release];
return NO;
}
In your TableViewController, add:
- (id<UITableViewDelegate>)createDelegate {
return self;
}
Then you can implement your own didSelectRowAtIndexPath and accessoryButtonTappedForRowWithIndexPath methods.