在视图控制器之间传递一个到多个核心数据
我在视图控制器之间传递数据时遇到问题。
所有三个视图控制器都是表视图。
WorkoutTypeVC WorkoutSetVC WorkoutExerciseVC
我有三个实体,
WorkoutType workouts(->WorkoutSet) One to Many WorkoutSet exercises(->WorkoutExercise) One to Many workoutType(->WorkoutType) Inverse WorkoutExercise workoutSet(->WorkoutSet) Inverse
我可以在所有三个视图控制器之间切换,WorkoutTypeVC正确加载正确显示所有条目,当选择WorkoutSetVC时,加载显示与WorkoutTypeVC选择对应的正确条目。
但是当我从WorkoutSetVC中选择一个条目时,WorkoutExerciseVC加载但是为空,甚至不加载选择的标题。
我使用了从WorkoutTypeVC和WorkoutSetVC切换时使用的相同代码。
下面是在WorkoutType.m文件中切换视图的代码:
-(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]; }
下面是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]; }
下面是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; }
不确定我需要做什么让第三个视图控制器列出所有条目,甚至第三个视图控制器的标题也不加载在ViewDidLoad方法中编码的。
谢谢
问题是(我假设)在第二个(和第三个?)视图控制器中不一致地使用数据源。
在WorkoutSetViewController
, cellForRowAtIndexPath
直接通过self.workoutType.workouts.allObjects
访问对象,但didSelectRowAtIndexPath
使用获取的结果控制器(FRC)。 这根本不符合逻辑。 如果表视图由FRC驱动,则所有数据源方法都必须使用FRC。
也许self.fetchedResultsController
在第二个视图控制器中是nil
? 然后传递给第三个视图控制器的workoutSet
将为nil
,这将解释没有设置标题并且不显示任何对象。
并且使用allObjects
从self.workoutType.workouts
生成数组也是有问题的,因为数组元素的顺序可以是随机的。
第二个视图控制器应使用获取的结果控制器来显示与给定的workoutType
相关的所有WorkoutSet
对象。
第三个视图控制器应使用获取的结果控制器来显示与给定的workoutSet
相关的所有WorkoutExercise
对象。
更新: fetchWorkoutSets
中的fetchWorkoutSets应如下所示:
-(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); } }
谓词对于仅获取与self.workoutType
相关的训练集非常重要。
同样, fetchWorkoutExercises
中的fetchWorkoutExercises将使用谓词
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"workoutSet = %@", self.workoutSet]; [fetchRequest setPredicate:predicate];
仅获取与self.workoutSet
相关的self.workoutSet
。
对于数据源方法,请查看NSFetchedResultsController文档 ,它包含可以复制并适应您需求的示例代码。 或者使用“Master-Detail Application”模板创建一个全新的Xcode iOS应用程序,然后选中“Core Data”复选框。 这也将为您提供示例代码。
例如,在WorkoutSetVC.m中的cellForRowAtIndexPath
中,您将替换
WorkoutSet *workoutSet = [self.workoutType.workouts.allObjects objectAtIndex:indexPath.row];
通过
WorkoutSet *workoutSet = [self.fetchedResultsController objectAtIndexPath:indexPath];