如何准备与NSFetchedResults一起深入查看具有目标c的另一个tableview

时间:2016-08-27 23:58:24

标签: objective-c uitableview nspredicate nsfetchedresultscontroller uistoryboardsegue

我知道可能有一个非常简单的解决方案。我正在使用apple iphoneCoreDataRecipes示例。我正在尝试从RecipeTypes的初始表(RecipeTypeTableViewController)向下钻取(即Beverage,Dessert,Entree)到下一个tableview,其中列出了这些类型的Recipes(RecipeListTableViewController)。我创建了一个新类RecipeType,我想我可以使用它来返回所选RecipeType的配方。到目前为止,我能得到的最好的是允许选择但它显示所有食谱的相同列表。原始代码以RecipeListTableViewController开始,使用NSFetchedResults显示所有配方。现在我有了RecipeTypes的初始视图,但我不确定如何将所选类型传递给RecipeListTableViewController。我需要更改RecipeListTableViewController以显示所选RecipeType的列表,但我不知道如何实现它。我想我基本上想把一个带有fetchedresults的NSMutableArray传递给另一个tableview,但不知道要采取哪个方向。而且我不想搞砸RecipeListTableViewController中的其他工作动作,即搜索和编辑食谱。这是迄今为止的表格动作的图像[RecipeTypeTableView] [1]& [RecipeListTableview] [2]。提前感谢您的任何建议。

删除了这些

文件:RecipeType.h

文件:RecipeType.m

我使用Recipe Class NSManagedObject中的NSManagedObject *类型在RecipeTypeTableViewController上创建配方类型的初始视图(Appetizer,Bev等) -

文件:Recipe.h

@interface Recipe : NSManagedObject
@property (nonatomic, strong) NSManagedObject *type;

文件:RecipeTypeTableViewController.h

//  RecipeTypeTableViewController.h
//  Recipes
#import <UIKit/UIKit.h>
@interface RecipeTypeTableViewController : UITableViewController <NSFetchedResultsControllerDelegate>
@property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (nonatomic, strong) NSManagedObject *type;
@end

文件:RecipeTypeTableViewController.m - 准备segue部分

#import "RecipeTypeTableViewController.h"
#import "Recipe.h"
#import "RecipeListTableViewController.h"
@interface RecipeTypeTableViewController () <NSFetchedResultsControllerDelegate>
@property (nonatomic, strong) NSFetchedResultsController *fetchedResultsController;
@end

@implementation RecipeTypeTableViewController

static NSString *MyIdentifier = @"showRecipes";

- (void)viewDidLoad {
[super viewDidLoad];
// register this class for our cell to this table view under the specified identifier 'ARecipeType'
self.title = @"Category";
//[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"ARecipeType"];

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didChangePreferredContentSize:)name:UIContentSizeCategoryDidChangeNotification object:nil];
// 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;
NSError *error = nil;
if (![[self fetchedResultsController] performFetch:&error]) {

    NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
    //abort();
}
}

- (void)viewWillAppear:(BOOL)animated {

[super viewWillAppear:animated];
self.title = @"Category";

}

- (void)didChangePreferredContentSize:(NSNotification *)notification
{
[self.tableView reloadData];
}

- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIContentSizeCategoryDidChangeNotification object:nil];
}
#pragma mark - Table view data source

//- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
//#warning Incomplete implementation, return the number of sections
//return [[self.recipeTypeFetchedResultsController sections] count];

//    return 1;
//}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Number of rows is the number of recipe types
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
//return self.recipeTypes.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ARecipeType" forIndexPath:indexPath];

// Configure the cell
NSManagedObject *recipeType = [self.recipeTypes objectAtIndex:indexPath.row];
cell.textLabel.text = [recipeType valueForKey:@"name"]; 
return cell;
}

#pragma mark - Fetched results controller

- (NSFetchedResultsController *)fetchedResultsController {

// Set up the fetched results controller if needed.
if (_fetchedResultsController == nil) {
    // Create the fetch request for the entity.
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    // Edit the entity name as appropriate.
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"RecipeType" inManagedObjectContext:self.managedObjectContext];
    [fetchRequest setEntity:entity];
    NSLog(@"Setting up a Fetched Results Controller for the Entity named %@", entity);

    // Edit the sort key as appropriate.
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];
    NSArray *sortDescriptors = [[NSArray alloc] initWithObjects: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:@"Recipe"];
    aFetchedResultsController.delegate = self;
    self.fetchedResultsController = aFetchedResultsController;


    NSError *error = nil;
    if (![self.fetchedResultsController performFetch:&error])
    {
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
    }
}

return _fetchedResultsController;
}

#pragma mark - Navigation

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:@"showRecipes"]) {
    NSLog(@"Setting RecipeType for the RecipeListTableViewController");
    NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
    Recipe *selectedType = [self.fetchedResultsController objectAtIndexPath:indexPath];
    RecipeListTableViewController *recipeListViewController = segue.destinationViewController;
    recipeListViewController.type = selectedType;
    recipeListViewController.managedObjectContext = self.managedObjectContext;
}
}

文件:RecipeListTableViewController.h - NSPredicate部分和FRC缓存

#import <UIKit/UIKit.h>
#import "RecipeAddViewController.h"

@interface RecipeListTableViewController : UITableViewController <RecipeAddDelegate, NSFetchedResultsControllerDelegate>

@property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (nonatomic, strong) NSManagedObject *type;

文件:RecipeListTableViewController.m

#import "RecipeListTableViewController.h"
#import "RecipeDetailViewController.h"
#import "Recipe.h"
#import "RecipeTableViewCell.h"
#import "Recipe+Extensions.h"
#import "TypeSelectionViewController.h"
#import "IngredientDetailViewController.h"
#import "Ingredient.h"
#import "WhereViewController.h"
#import "FavoriteListTableViewController.h"
#import "RecipeTypeTableViewController.h"
#import "RecipeType.h"


@interface RecipeListTableViewController () <NSFetchedResultsControllerDelegate, UISearchBarDelegate, UISearchResultsUpdating>

@property (nonatomic, strong) NSFetchedResultsController *fetchedResultsController;
@property (nonatomic, strong) NSArray *filteredList;
@property (nonatomic, strong) NSFetchRequest *searchFetchRequest;
@property (nonatomic, strong) UISearchController *searchController;

typedef NS_ENUM(NSInteger, RecipesSearchScope)
{
searchScopeRecipe = 0,
searchScopeIngredients = 1
};


@end

@implementation RecipeListTableViewController


#pragma mark - Fetched results controller

- (NSFetchedResultsController *)fetchedResultsController {

// Set up the fetched results controller if needed.
if (_fetchedResultsController == nil) {
    // Create the fetch request for the entity.
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    // Edit the entity name as appropriate.
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Recipe" inManagedObjectContext:self.managedObjectContext];
    [fetchRequest setEntity:entity];

[NSFetchedResultsController deleteCacheWithName:@"Recipe"];

    // Create predicate
    //if (self.type) {
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"type == %@", self.type];
    [fetchRequest setPredicate:predicate];
    //}


    // Edit the sort key as appropriate.
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];
    NSArray *sortDescriptors = [[NSArray alloc] initWithObjects: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:@"Recipe"];
    aFetchedResultsController.delegate = self;
    self.fetchedResultsController = aFetchedResultsController;

    NSError *error = nil;
    if (![self.fetchedResultsController performFetch:&error])
    {
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
    }
}

return _fetchedResultsController;
}

/

当我将FRC缓存设置为“Recipe”时。它崩溃了。它显示了查看缓存CoreData的这一线索:致命错误:部分信息的持久缓存与当前配置不匹配。你非法改变了NSFetchedResultsController的获取请求,它的谓词或它的排序描述......

如果我将缓存设置为nil,或者添加[NSFetchedResultsController deleteCacheWithName:@“Recipe”];在谓词集之前,事情按预期工作。我在初始视图控制器和第二个视图控制器中有这个缓存。也许这就是问题 - 我只需要在初始视图控制器中缓存?

1 个答案:

答案 0 :(得分:0)

首先,我认为您的数据模型需要稍微改进一下。每个RecipeType可能会有许多关联Recipes。因此,从RecipeTypeRecipe的关系应该是 to-many 。如果在数据模型编辑器中更改此项并重新生成模型类,则应该有一个类似于此的RecipeType类:

@class Recipe;

@interface RecipeType : NSManagedObject
// not sure what purpose this property serves; it might now be superfluous...
@property (nonatomic, strong) NSManagedObject *type;

@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSSet<Recipe *> *recipes;

@end 

我暂时假设每个Recipe只能属于一个RecipeType。因此,从RecipeRecipeType的反比关系应为 to-one ,因此Recipe类将具有属性:

*property (nonatomic, strong) RecipeType *type;

接下来,您希望RecipeListTableViewController仅显示与相关Recipes相关的RecipeType。要实现这一点,您需要向获取的结果控制器添加谓词。在fetchedResultsController方法中,添加:

if (self.recipeType) {
    fetchRequest.predicate = [NSPredicate predicateWithFormat:@"type == %@", self.recipeType];
}

(您还需要修改搜索,同样将搜索限制为相关的RecipeType)。在RecipeTypeTableViewContoller中,您的prepareForSegue只需传递正确的RecipeType

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {    
    if ([[segue identifier] isEqualToString:@"showRecipes"]) {
        NSLog(@"Setting RecipeType for the RecipeListTableViewController");
        NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
        RecipeType *selectedType = [self.fetchedResultsController objectAtIndexPath:indexPath];
        RecipeListTableViewController *recipeListViewController = segue.destinationViewController;
        recipeListViewController.recipeType = selectedType;
        recipeListViewController.managedObjectContext = self.managedObjectContext;
    }
}

每当您添加新的Recipe时,您都需要将其分配给正确的RecipeType。因此,在RecipeListViewController修改您的prepareForSegue以设置关系:

...
else if ([segue.identifier isEqualToString:kAddRecipeSegueID]) {
    // add a recipe
    //
    Recipe *newRecipe = [NSEntityDescription insertNewObjectForEntityForName:@"Recipe" inManagedObjectContext:self.managedObjectContext];
    newRecipe.type = self.recipeType;

    UINavigationController *navController = segue.destinationViewController;
    RecipeAddViewController *addController = (RecipeAddViewController *)navController.topViewController;
    addController.delegate = self;  // do didAddRecipe delegate method is called when cancel or save are tapped
    addController.recipe = newRecipe;
}