Display managed object in two sections of NSFetchedResultsController - core-data

I have an NSManagedObject which has three attributes:
Header (NSString *)
Title (NSString *)
Favorite (BOOL)
I'd like to display the list of these objects using the following scheme:
Favorites
Object 1
Object 3
A Header
Object 2
Object 3
B Header
Object 1
Object 4
Is there any way to do that using NSFetchedResultsController? I tried sorting it by favorite, header to no avail since once object is assigned to favorites section - it won't be displayed in section of its header. Is there any trick that I can use? Should I perform two fetches and reformat the results into one nested array?

Use two separate NSFetchedResultsController's
Then you need to account for this in each of the various delegate methods.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [[self.mainFetchedResultsController sections] count] + 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (section == 0) {
return [[[self.favFetchedResultsController sections] objectAtIndex:section] numberOfObjects];
} else {
return [[[self.mainFetchedResultsController sections] objectAtIndex:section - 1] numberOfObjects];
}
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
if (section == 0) {
return #"Favourites";
} else {
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.mainFetchedResultsController sections] objectAtIndex:section - 1];
return [sectionInfo name];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
Object *object = nil;
if (indexPath.section == 0) {
object = [self.favFetchedResultsController objectAtIndexPath:indexPath];
} else {
NSIndexPath *mainIndexPath = [NSIndexPath indexPathForRow:indexPath.row inSection:indexPath.section -1];
object = [self.mainFetchedResultsController objectAtIndexPath:mainIndexPath];
}
UITableViewCell *cell = ...
...
return cell;
}

Related

NSFetchedResultsController: problems when having two table rows for one object

I've got a tableView where each data set has two rows, therefore my datasource-methods look like the following:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return self.fetchedResultsController.sections.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)sectionIndex {
id <NSFetchedResultsSectionInfo> section = self.fetchedResultsController.sections[sectionIndex];
return section.numberOfObjects * 2;
}
The NSFetchedResultsControllerDelegate method:
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
if (NSFetchedResultsChangeDelete == type) {
NSIndexPath *cellIndexPath = indexPath;
NSIndexPath *separatorIndexPath = [NSIndexPath indexPathForRow:indexPath.row + 1 inSection:indexPath.section];
[self.transactionTableView deleteRowsAtIndexPaths:#[cellIndexPath, separatorIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
} else if (NSFetchedResultsChangeInsert == type) {
NSIndexPath *cellIndexPath = newIndexPath;
NSIndexPath *separatorIndexPath = [NSIndexPath indexPathForRow:newIndexPath.row + 1 inSection:newIndexPath.section];
[self.transactionTableView insertRowsAtIndexPaths:#[cellIndexPath, separatorIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
} else {
[self.transactionTableView reloadData];
}
}
If I only use one row for each dataset, everything works as expected. But as soon as I make it this way, I get NSAssertionErrors here:
- (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];
}
Isn't it possible to use two rows for one data set? What am I doing wrong? I would have expected to work it this way as I always insert two indexPaths whenever the underlying core data changes.
That should definitely be possible, but your mapping from the FRC index path to the
table view index path looks wrong. It should probably be
NSIndexPath *cellIndexPath = [NSIndexPath indexPathForRow:(2*indexPath.row) inSection:indexPath.section];
NSIndexPath *separatorIndexPath = [NSIndexPath indexPathForRow:(2*indexPath.row + 1) inSection:indexPath.section];

How to use groupBy with MagicalRecord?

I am stuck on how to use groupBy with MagicalRecord.
I have a list of countries with venues
Country -<< Venues
I need to group all the venues by the country and sort the countries by name.
But I am not sure how to do this with MagicalRecord.
I have tried to use a NSFetchedController but sometimes it crashes saying that the array is nil or 0 length.
Other times, it only ever sees 1 category when there are multiple.
Finally, I am not sure how to execute the fetch on an entity.
ie;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
_objects = [NSMutableArray array];
self.fetchedResultsController = [self fetchedResultsController];
[Venue performFetch:self.fetchedResultsController];
// At this point how do I make the Venue findAllSortedBy work on the performFetch?
_objects = [NSMutableArray arrayWithArray:[Venue findAllSortedBy:#"name" ascending:YES inContext:[NSManagedObjectContext defaultContext]]];
self.title = #"Venues";
}
- (NSFetchedResultsController *)fetchedResultsController {
if (!fetchedResultsController) {
fetchedResultsController = [Venue fetchAllSortedBy:#"name"
ascending:YES
withPredicate:nil
groupBy:#"country"
delegate:self];
}
return fetchedResultsController;
}
-(NSString*)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
NSLog(#"Section = %d", section);
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
NSLog(#"sectionInfo = %#", sectionInfo);
return #"Header";
}
- (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tv dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
Venue *v = [_objects objectAtIndex:indexPath.row];
cell.textLabel.text = v.name;
}
I am not sure if I am doing this right.
The above will put everything in 1 country (when there are multiple) and the log will report;
CoreData: error: (NSFetchedResultsController) A section returned nil value for section
name key path 'country'. Objects will be placed in unnamed section
It seems not to see the different countries and I do not think I've done the GroupBy command correctly.
Thus, how do I do a GroupBy command with MagicalRecord?
Many thanks.
self.fetchedVenues = [Venue MR_fetchAllGroupedBy:#"country" withPredicate:nil sortedBy:#"name" ascending:YES];
This error is telling you that of all your Venue objects, there is at least one in your result set that does not have a value for "country". You need to verify that you are indeed filling in this field and saving it properly prior to fetching.
And FYI, in your viewDidLoad method, you don't need all that code. Simply do something like:
- (void) viewDidLoad;
{
[super viewDidLoad];
self.fetchedVenues = [Venue fetchAllSortedBy:#"name" ascending:YES withPredicate:nil groupBy:#"country" delegate:self];
}
fetchAllSortedBy... will perform the fetch for you, and log errors, etc. That is the point of a helper framework like MagicalRecord.

How to get result from Core Data search/filter?

I need some help searching / filtering a Core data entity. The results array returns Null..
I have a root view with a search bar, controller and tableview. This view shows normally.
I'm calling the UISearchBarDelegate and the UISearchDisplayDelegate.
I have a mutable array (searchResults).
My search code is as follows:
-(void) filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
NSLog(#"%s", __FUNCTION__);
[self.searchResults removeAllObjects];
for (Entity *ent in [self.fetchedResultsController fetchedObjects])
{
if ([scope isEqualToString:#"All"] || [ent.title isEqualToString:scope])
{
NSRange range = [ent.title rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (range.location != NSNotFound)
{
NSLog(#"Adding title '%#' to searchResults as it contains '%#'", ent.title, searchText);
[self.searchResults addObject:ent];
}
}
}
NSLog(#"The searchResults array contains '%#'", searchResults); <<<< RETURNS NULL
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterContentForSearchText:searchString scope:#"All"];
return YES;
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption
{
[self filterContentForSearchText:[self.searchDisplayController.searchBar text ]scope:#"All"];
return YES;
}
and the cell config code is:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"MyVeryOwnCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell...
Entity *entity = nil;
if (tableView == self.searchDisplayController.searchResultsTableView)
{
//NSLog(#"Configuring cell to show search results");
entity = [self.searchResults objectAtIndex:indexPath.row];
}
else
{
//NSLog(#"Configuring cell to show normal data");
entity = [self.fetchedResultsController objectAtIndexPath:indexPath];
}
cell.textLabel.text = entity.title;
return cell;
}
I must be doing something dumb as the searchResults array appears to be null. I would appreciate any advice.
You just forgot to alloc/init the searchResults array.
;-)

Multiple index paths

I need to implement two different index paths on my tableview in cellForRow at index path... One with data from my fetched results controller and another for simple text.
What is the best way to go about this.
Im not exactly sure what your asking, but it sounds like you want a table with one section containing results from an NSFetchedResultController, and another section with data from some other source. This is easy to do. If your data can doesn't need to be in multiple sections then it's very easy, just get data from fetchedResultController for section 0, and something else for section 1.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 2;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
if(section == 0){
id sectionInfo = [[fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}else{
return self.someArray.Count;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
if(indexpath.section == 0){
static NSString *CellIdentifier = #"data cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
NSObject *foo = [self.fetchedResultController objectAtIndexPath:indexPath];
//configure cell
return cell;
}else{
static NSString *CellIdentifier = #"some other cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
//configure cell
return cell;
}
}

Is there anyway to populate a UITableView with an array but persist checkmarks?

I have an app that has a tab bar, each tab contains a separate table. The first table uses core data to persist its entries and checkmarks. The second table on the second tab uses an NSMutableArray to populate it (I would use core data but I would have to pre populate it and this table does not allow that) I would like to persist check marks the same way I do in the first table with core data but something is wrong. The code looks like this:
-(void)viewDidLoad {
[super viewDidLoad];
airport = [[NSMutableArray alloc] init];
[airport addObject:#"Passport"];
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [airport 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];
}
// Set up the cell...
NSString *cellValue = [airport objectAtIndex:indexPath.row];
cell.textLabel.text = cellValue;
//[cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator];
NSManagedObject *item = [[self fetchedResultsController] objectAtIndexPath:indexPath];
cell.textLabel.text = [item valueForKey:#"name"]; //CHANGED TO DETAIL
if ([[item valueForKey:#"check"] boolValue]) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
cell.selectionStyle = UITableViewCellSelectionStyleNone;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSManagedObject *selectedObject = [self.fetchedResultsController objectAtIndexPath:indexPath];
if ([[selectedObject valueForKey:#"check"] boolValue]) {
[selectedObject setValue:[NSNumber numberWithBool:NO] forKey:#"check"];
} else {
[selectedObject setValue:[NSNumber numberWithBool:YES] forKey:#"check"];
}
UITableViewCell *thisCell = [tableView cellForRowAtIndexPath:indexPath];
if (thisCell.accessoryType == UITableViewCellAccessoryNone) {
thisCell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
thisCell.accessoryType = UITableViewCellAccessoryNone;
}
[tableView deselectRowAtIndexPath:indexPath animated:NO];
}
I believe the line cell.textLabel.text = [item valueForKey#"name"];
is whats causing it. All that I would like for this to do is have the table populated from the array and check marks persisted. Any help is GREATLY appreciated. Thanks in advance.
This line is replacing the cell text:
cell.textLabel.text = [item valueForKey:#"name"];
that was initially assigned by these lines:
NSString *cellValue = [airport objectAtIndex:indexPath.row];
cell.textLabel.text = cellValue;
Why are your using the "name" property of "item"?

Resources