在视图控制器之间传递一个到多个核心数据 [英] Passing one to many core data between view controllers
问题描述
我在视图控制器之间传递数据时出现问题。
所有三个视图控制器都是表视图。
WorkoutTypeVC
WorkoutSetVC
WorkoutExerciseVC
我有三个实体,
WorkoutType
workouts( - > WorkoutSet)一到多
WorkoutSet
exercise( - > WorkoutExercise)一对多
workoutType( - > WorkoutType)Inverse
WorkoutExercise
workoutSet( - > WorkoutSet)Inverse
我可以在所有三个视图控制器之间切换,WorkoutTypeVC加载正确显示所有条目,当选择WorkoutSetVC加载显示正确的条目对应于
但是当我从WorkoutSetVC中选择一个条目时,WorkoutExerciseVC加载但是是空的,即使选择的标题不加载。
我使用了与从WorkoutTypeVC和WorkoutSetVC切换时使用的代码相同的代码。
下面是在WorkoutType.m文件中切换视图的代码:
- (void)fetchWorkoutTypes
{
NSFetchRequest * fetchRequest =
[NSFetchRequest fetchRequestWithEntityName:@WorkoutType];
NSString * cacheName = [@WorkoutTypestringByAppendingString:@Cache];
NSSortDescriptor * sortDescriptor =
[NSSortDescriptor sortDescriptorWithKey:@workoutTypeascending:YES];
[fetchRequest setSortDescriptors:@ [sortDescriptor]];
self.fetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil cacheName:cacheName];
NSError * error;
if(![self.fetchedResultsController performFetch:& error])
{
NSLog(@Fetch failed:%@,error);
}
}
- (void)viewDidAppear:(BOOL)animated {
[self fetchWorkoutTypes];
[self.tableView reloadData];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
//返回节的数量。
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
//返回行数在部分。
return self.fetchedResultsController.fetchedObjects.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString * CellIdentifier = @Cell ;
UITableViewCell * cell =
[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1
reuseIdentifier:CellIdentifier];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
WorkoutType * workoutType =(WorkoutType *)[self.fetchedResultsController
objectAtIndexPath:indexPath];
cell.textLabel.text = workoutType.workoutType;
cell.detailTextLabel.text = [NSString stringWithFormat:@(%d),workoutType.workouts.count];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
WorkoutType * workoutType = .fetchedResultsController objectAtIndexPath:indexPath];
WorkoutSetViewController * detailViewController = [[WorkoutSetViewController alloc] initWithWorkoutType:workoutType];
[self.navigationController pushViewController:detailViewController animated:YES];
}
下面是WorkoutSetVC.m的代码
- (void)fetchWorkoutSets
{
NSFetchRequest * fetchRequest =
[NSFetchRequest fetchRequestWithEntityName:@WorkoutSet ];
NSString * cacheName = [@WorkoutSetstringByAppendingString:@Cache];
NSSortDescriptor * sortDescriptor =
[NSSortDescriptor sortDescriptorWithKey:@workoutNameascending:YES];
[fetchRequest setSortDescriptors:@ [sortDescriptor]];
self.fetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil cacheName:cacheName];
NSError * error;
if(![self.fetchedResultsController performFetch:& error])
{
NSLog(@Fetch failed:%@,error);
}
}
- (id)initWithWorkoutType:(WorkoutType *)workoutType
{
self = [super initWithStyle:UITableViewStylePlain];
if(self)
{
self.workoutType = workoutType;
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.title = self.workoutType.workoutType;
[self fetchWorkoutSets];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
//返回节的数量。
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
//返回节中的行数。
return self.workoutType.workouts.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString * CellIdentifier = @细胞;
UITableViewCell * cell =
[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:CellIdentifier];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
WorkoutSet * workoutSet = [self.workoutType.workouts.allObjects objectAtIndex:indexPath.row];
cell.textLabel.text = workoutSet.workoutName;
cell.detailTextLabel.text = [NSString stringWithFormat:@(%d),workoutSet.exercises.count];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
WorkoutSet * workoutSet =(WorkoutSet *)[self.fetchedResultsController objectAtIndexPath: indexPath];
WorkoutExerciseTableViewController * detailViewController = [[WorkoutExerciseTableViewController alloc] initWithWorkoutSet:workoutSet];
[self.navigationController pushViewController:detailViewController animated:YES];
}
下面是WorkoutExercise.m的代码
- (id)initWithWorkoutSet:(WorkoutSet *)workoutSet
{
self = [super initWithStyle:UITableViewStylePlain];
if(self)
{
self.workoutSet = workoutSet;
}
return self;
}
- (void)fetchWorkoutExercises
{
NSFetchRequest * fetchRequest =
[NSFetchRequest fetchRequestWithEntityName:@WorkoutExercise];
NSString * cacheName = [@WorkoutExercisestringByAppendingString:@Cache];
NSSortDescriptor * sortDescriptor =
[NSSortDescriptor sortDescriptorWithKey:@exerciseNameascending:YES];
[fetchRequest setSortDescriptors:@ [sortDescriptor]];
self.fetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil cacheName:cacheName];
NSError * error;
if(![self.fetchedResultsController performFetch:& error])
{
NSLog(@Fetch failed:%@,error);
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.title = self.workoutSet.workoutName;
[self fetchWorkoutExercises];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString * CellIdentifier = @Cell;
UITableViewCell * cell =
[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:CellIdentifier];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
WorkoutExercise * exercise = [self.workoutSet.exercises.allObjects objectAtIndex:indexPath.row];
cell.textLabel.text = exercise.exerciseName;
return cell;
}
不知道我需要做什么,
谢谢
所有条目,甚至第三个视图控制器的标题都不会加载在ViewDidLoad方法中。 (我假设)第二个(和第三个)视图控制器中数据源的使用不一致。
在您的 WorkoutSetViewController
中, cellForRowAtIndexPath
通过 self.workoutType.workouts.allObjects
,但 didSelectRowAtIndexPath
使用提取的结果控制器(FRC)。这没有意义。如果表视图由FRC驱动,所有数据源方法必须使用FRC。
可能 self.fetchedResultsController
nil
在第二个视图控制器?
然后,传递给第三个视图控制器的 workoutSet
将是 nil
,这将
并从 self.workoutType.workouts
生成一个数组 allObjects
也有问题,因为数组元素的顺序可以是随机的。
第二个视图控制器应该使用获取结果控制器以显示与给定 workoutType
相关的
all WorkoutSet
对象。
而第三个视图控制器应该使用获取的结果控制器来显示与给定的<$ c相关的
all WorkoutExercise
$ c> workoutSet 。
UPDATE: fetchWorkoutSets
在WorkoutSetVC.m中应该看起来像这样:
- (void)fetchWorkoutSets
{
NSFetchRequest * fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@WorkoutSet];
NSPredicate * predicate = [NSPredicate predicateWithFormat:@workoutType =%@,self.workoutType];
[fetchRequest setPredicate:predicate];
NSSortDescriptor * sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@workoutNameascending:YES];
[fetchRequest setSortDescriptors:@ [sortDescriptor]];
self.fetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil cacheName:nil];
self.fetchedResultsController.delegate = self;
NSError * error;
if(![self.fetchedResultsController performFetch:& error])
{
NSLog(@Fetch failed:%@,error);
}
}
以获取与 self.workoutType
相关的锻炼集。
同样, WorkoutExercise.m中的fetchWorkoutExercises
将使用谓词
NSPredicate * predicate = [NSPredicate predicateWithFormat:@ workoutSet =%@,self.workoutSet];
[fetchRequest setPredicate:predicate];
只取得与 self.workoutSet
。
有关数据源方法,请查看 NSFetchedResultsController文档,它包含示例代码,您可以复制
并适应您的需要。或者,使用主 - 详细应用程序模板创建一个新的Xcode iOS应用程序,并选中核心数据复选框。这也将给你
样例代码。
例如,在WorkoutSetVC.m中的 cellForRowAtIndexPath
将替换
WorkoutSet * workoutSet = [self.workoutType.workouts.allObjects objectAtIndex:indexPath.row];
由
WorkoutSet * workoutSet = [self.fetchedResultsController objectAtIndexPath:indexPath];
I am having problems passing data between view controllers.
All three view controllers are table views.
WorkoutTypeVC
WorkoutSetVC
WorkoutExerciseVC
I have three entities,
WorkoutType
workouts(->WorkoutSet) One to Many
WorkoutSet
exercises(->WorkoutExercise) One to Many
workoutType(->WorkoutType) Inverse
WorkoutExercise
workoutSet(->WorkoutSet) Inverse
I am able to switch between all three view controllers, WorkoutTypeVC loads correctly showing all entries, When selected WorkoutSetVC is loaded showing the correct entries corresponding to the selection made from WorkoutTypeVC.
But when i select an entry from WorkoutSetVC, WorkoutExerciseVC loads but is empty, Even the title of the selection doesn't load.
I have used the same code which i used when switching from WorkoutTypeVC and WorkoutSetVC.
Below is the code for switching views in WorkoutType.m file:
-(void)fetchWorkoutTypes
{
NSFetchRequest *fetchRequest =
[NSFetchRequest fetchRequestWithEntityName:@"WorkoutType"];
NSString *cacheName = [@"WorkoutType" stringByAppendingString:@"Cache"];
NSSortDescriptor *sortDescriptor =
[NSSortDescriptor sortDescriptorWithKey:@"workoutType" ascending:YES];
[fetchRequest setSortDescriptors:@[sortDescriptor]];
self.fetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil cacheName:cacheName];
NSError *error;
if (![self.fetchedResultsController performFetch:&error])
{
NSLog(@"Fetch failed: %@", error);
}
}
- (void)viewDidAppear:(BOOL)animated{
[self fetchWorkoutTypes];
[self.tableView reloadData];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return self.fetchedResultsController.fetchedObjects.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell =
[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1
reuseIdentifier:CellIdentifier];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
WorkoutType *workoutType = (WorkoutType *)[self.fetchedResultsController
objectAtIndexPath:indexPath];
cell.textLabel.text = workoutType.workoutType;
cell.detailTextLabel.text = [NSString stringWithFormat:@"(%d)", workoutType.workouts.count];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
WorkoutType *workoutType = (WorkoutType *)[self.fetchedResultsController objectAtIndexPath:indexPath];
WorkoutSetViewController *detailViewController = [[WorkoutSetViewController alloc] initWithWorkoutType:workoutType];
[self.navigationController pushViewController:detailViewController animated:YES];
}
Below is the code for WorkoutSetVC.m
-(void)fetchWorkoutSets
{
NSFetchRequest *fetchRequest =
[NSFetchRequest fetchRequestWithEntityName:@"WorkoutSet"];
NSString *cacheName = [@"WorkoutSet" stringByAppendingString:@"Cache"];
NSSortDescriptor *sortDescriptor =
[NSSortDescriptor sortDescriptorWithKey:@"workoutName" ascending:YES];
[fetchRequest setSortDescriptors:@[sortDescriptor]];
self.fetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil cacheName:cacheName];
NSError *error;
if (![self.fetchedResultsController performFetch:&error])
{
NSLog(@"Fetch failed: %@", error);
}
}
- (id)initWithWorkoutType:(WorkoutType *)workoutType
{
self = [super initWithStyle:UITableViewStylePlain];
if (self)
{
self.workoutType = workoutType;
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.title = self.workoutType.workoutType;
[self fetchWorkoutSets];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return self.workoutType.workouts.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell =
[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:CellIdentifier];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
WorkoutSet *workoutSet = [self.workoutType.workouts.allObjects objectAtIndex:indexPath.row];
cell.textLabel.text = workoutSet.workoutName;
cell.detailTextLabel.text = [NSString stringWithFormat:@"(%d)", workoutSet.exercises.count];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
WorkoutSet *workoutSet = (WorkoutSet *)[self.fetchedResultsController objectAtIndexPath:indexPath];
WorkoutExerciseTableViewController *detailViewController = [[WorkoutExerciseTableViewController alloc] initWithWorkoutSet:workoutSet];
[self.navigationController pushViewController:detailViewController animated:YES];
}
Below is the code for WorkoutExercise.m
- (id)initWithWorkoutSet:(WorkoutSet *)workoutSet
{
self = [super initWithStyle:UITableViewStylePlain];
if (self)
{
self.workoutSet = workoutSet;
}
return self;
}
-(void)fetchWorkoutExercises
{
NSFetchRequest *fetchRequest =
[NSFetchRequest fetchRequestWithEntityName:@"WorkoutExercise"];
NSString *cacheName = [@"WorkoutExercise" stringByAppendingString:@"Cache"];
NSSortDescriptor *sortDescriptor =
[NSSortDescriptor sortDescriptorWithKey:@"exerciseName" ascending:YES];
[fetchRequest setSortDescriptors:@[sortDescriptor]];
self.fetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil cacheName:cacheName];
NSError *error;
if (![self.fetchedResultsController performFetch:&error])
{
NSLog(@"Fetch failed: %@", error);
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.title = self.workoutSet.workoutName;
[self fetchWorkoutExercises];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell =
[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:CellIdentifier];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
WorkoutExercise *exercise = [self.workoutSet.exercises.allObjects objectAtIndex:indexPath.row];
cell.textLabel.text = exercise.exerciseName;
return cell;
}
Not sure as to what i need to do for the third view controller to list all the entries, Even the title for the third view controller doesn't load which is coded in the ViewDidLoad Method.
Thank You
The problem is (I assume) the inconsistent use of data sources in the second (and third?) view controller.
In your WorkoutSetViewController
, cellForRowAtIndexPath
accesses the objects directly via self.workoutType.workouts.allObjects
, but didSelectRowAtIndexPath
uses a fetched results controller (FRC). This does not make sense. If the table view is driven by a FRC, all data source methods must use the FRC.
Perhaps self.fetchedResultsController
is nil
in the second view controller?
Then the workoutSet
passed to the third view controller would be nil
, which would
explain that no title is set and no objects are displayed.
And generating an array from self.workoutType.workouts
with allObjects
is also problematic, because the order of the array elements can be random.
The second view controller should use a fetched results controller to display
all WorkoutSet
objects related to the given workoutType
.
And the third view controller should use a fetched results controller to display
all WorkoutExercise
objects related to the given workoutSet
.
UPDATE: fetchWorkoutSets
in WorkoutSetVC.m should look like this:
-(void)fetchWorkoutSets
{
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"WorkoutSet"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"workoutType = %@", self.workoutType];
[fetchRequest setPredicate:predicate];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"workoutName" ascending:YES];
[fetchRequest setSortDescriptors:@[sortDescriptor]];
self.fetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil cacheName:nil];
self.fetchedResultsController.delegate = self;
NSError *error;
if (![self.fetchedResultsController performFetch:&error])
{
NSLog(@"Fetch failed: %@", error);
}
}
The predicate is important to fetch only workout sets that are related to self.workoutType
.
And similarly, fetchWorkoutExercises
in WorkoutExercise.m would use the predicate
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"workoutSet = %@", self.workoutSet];
[fetchRequest setPredicate:predicate];
to fetch only exercises that are related to self.workoutSet
.
For the data source methods, have a look at the NSFetchedResultsController documentation, it contains sample code that you can copy and adapt to your needs. Or you create a fresh Xcode iOS application with the "Master-Detail Application" template and select the "Core Data" checkbox. That will also give you sample code.
For example, in cellForRowAtIndexPath
in WorkoutSetVC.m you would replace
WorkoutSet *workoutSet = [self.workoutType.workouts.allObjects objectAtIndex:indexPath.row];
by
WorkoutSet *workoutSet = [self.fetchedResultsController objectAtIndexPath:indexPath];
这篇关于在视图控制器之间传递一个到多个核心数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!