我知道可能有一个非常简单的解决方案。我正在使用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”];在谓词集之前,事情按预期工作。我在初始视图控制器和第二个视图控制器中有这个缓存。也许这就是问题 - 我只需要在初始视图控制器中缓存?
答案 0 :(得分:0)
首先,我认为您的数据模型需要稍微改进一下。每个RecipeType
可能会有许多关联Recipes
。因此,从RecipeType
到Recipe
的关系应该是 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
。因此,从Recipe
到RecipeType
的反比关系应为 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;
}