我有一个简单的应用程序,每个UITabBarController
都有UITableViewControllers
和2 UITabBar
。第一个UITableViewController
称为Videos
,第二个称为Languages
。
Videos
将代表具有视频的语言,而语言将显示所有语言。
出于测试目的,我正在从AppDelegate填充核心数据实体。
我的核心数据模型如下:
填充核心数据的方法如下所示:
- (void)loadVideosTab
{
// Here is where we'll load up the Videos tab.
NSManagedObjectContext *context = [self managedObjectContext];
NSString *chinese = @"Chinese";
NSString *dutch = @"Dutch";
NSString *english = @"English";
NSString *french = @"French";
NSString *italian = @"Italian";
NSString *punjabi = @"Punjabi";
NSString *spanish = @"Spanish";
NSString *tamil = @"Tamil - தமிழ்";
Language *language = [NSEntityDescription insertNewObjectForEntityForName:@"Language" inManagedObjectContext:context];
Video *chineseLanguage = (Video *)[Video videoLanguage:chinese inManagedObjectContext:context];
language.videos = chineseLanguage;
Video *dutchLanguage = (Video *)[Video videoLanguage:dutch inManagedObjectContext:context];
language.videos = dutchLanguage;
Video *englishLanguage = (Video *)[Video videoLanguage:english inManagedObjectContext:context];
language.videos = englishLanguage;
Video *frenchLanguage = (Video *)[Video videoLanguage:french inManagedObjectContext:context];
language.videos = frenchLanguage;
Video *italianLanguage = (Video *)[Video videoLanguage:italian inManagedObjectContext:context];
language.videos = italianLanguage;
Video *punjabiLanguage = (Video *)[Video videoLanguage:punjabi inManagedObjectContext:context];
language.videos = punjabiLanguage;
Video *spanishLanguage = (Video *)[Video videoLanguage:spanish inManagedObjectContext:context];
language.videos = spanishLanguage;
Video *tamilLanguage = (Video *)[Video videoLanguage:tamil inManagedObjectContext:context];
language.videos = tamilLanguage;
NSError *error = nil;
if (![context save:&error])
{
// Error
}
}
我呼吁Video
实体的类别是:
+ (Video *)videoLanguage:(NSString *)name inManagedObjectContext:(NSManagedObjectContext *)context
{
Video *video = nil;
// Creating a fetch request to check whether the video already exists, calling from the AppDelegate
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Video"];
request.predicate = [NSPredicate predicateWithFormat:@"language = %@", name];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"language" ascending:YES];
request.sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSError *error = nil;
NSArray *videoTitles = [context executeFetchRequest:request error:&error];
if (!videoTitles)
{
// Handle Error
}
else if (![videoTitles count])
{
// If the video count is 0 then create it
video = [NSEntityDescription insertNewObjectForEntityForName:@"Video" inManagedObjectContext:context];
video.language = name;
}
else
{
// If the object exists, just return it
video = [videoTitles lastObject];
}
return video;
}
因此,在Video
标签中,我正在使用NSFetchedResultsController
:
- (NSFetchedResultsController *)fetchedResultsController
{
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
if (_fetchedResultsController != nil)
{
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Video" inManagedObjectContext:managedObjectContext];
fetchRequest.entity = entity;
NSPredicate *d = [NSPredicate predicateWithFormat:@"language != nil"];
[fetchRequest setPredicate:d];
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"language" ascending:YES];
fetchRequest.sortDescriptors = [NSArray arrayWithObject:sort];
fetchRequest.fetchBatchSize = 20;
NSFetchedResultsController *theFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:nil];
self.fetchedResultsController = theFetchedResultsController;
_fetchedResultsController.delegate = self;
return _fetchedResultsController;
}
而cellForRow
是:
VideoTabTableViewCell *customCell = (VideoTabTableViewCell *)cell;
Video *video = [self.fetchedResultsController objectAtIndexPath:indexPath];
customCell.videoTabCellLabel.text = video.language;
customCell.videoTabCellLabel.textColor = [UIColor whiteColor];
成功吸引语言。
现在,出于测试目的,我希望“语言”选项卡执行完全相同的操作。我有Language
实体的原因是因为语言标签是包含视频和传单的语言的组合。
因此,在Languages
标签中,我的fetchedResultsController
是:
- (NSFetchedResultsController *)fetchedResultsController
{
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
if (_fetchedResultsController != nil)
{
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Language" inManagedObjectContext:managedObjectContext];
fetchRequest.entity = entity;
// NSPredicate *d = [NSPredicate predicateWithFormat:@"name != nil"];
// [fetchRequest setPredicate:d];
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"videos.language" ascending:YES];
fetchRequest.sortDescriptors = [NSArray arrayWithObject:sort];
fetchRequest.fetchBatchSize = 20;
NSFetchedResultsController *theFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:nil];
self.fetchedResultsController = theFetchedResultsController;
_fetchedResultsController.delegate = self;
return _fetchedResultsController;
}
而cellForRow
是:
VideoTabTableViewCell *customCell = (VideoTabTableViewCell *)cell;
Language *languages = [self.fetchedResultsController objectAtIndexPath:indexPath];
customCell.videoTabCellLabel.text = languages.videos.language;
customCell.videoTabCellLabel.textColor = [UIColor whiteColor];
更新 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString * CellIdentifier = @“langauges tab”;
VideoTabTableViewCell *cell = (VideoTabTableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
id sectionInfo = [[_fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
[self.languagesTabTableView beginUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath
{
// The boiler plate code for the NSFetchedResultsControllerDelegate
UITableView *tableView = self.languagesTabTableView;
switch(type) {
case NSFetchedResultsChangeInsert:
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
[self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
default:
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
// The fetch controller has sent all current change notifications, so tell the table view to process all updates.
[self.languagesTabTableView endUpdates];
}
我从fetchedResultsController
拨打viewDidLoad
并从viewWillAppear
重新加载表格。
然而,Languages
标签上的情况是它只显示我添加的最后一种语言,每次启动时,我都会创建另一个新的空单元格。
我无法弄清楚这是怎么回事。
澄清,这里的主要问题不是幻影细胞,而是为什么语言只是拿起最后一个条目而不是其他条目。
任何指导都会非常感激。
答案 0 :(得分:1)
我不熟悉托管对象,但这看起来像字典。具有相同名称的多个insertNewObjectForEntityForName意味着最后一个将覆盖其余的。
Apple doc似乎也表明了这一点:
讨论
此方法使您可以轻松创建给定实体的实例,而无需担心托管对象创建的详细信息。该方法在概念上类似于以下代码示例。
NSManagedObjectModel *managedObjectModel =
[[context persistentStoreCoordinator] managedObjectModel];
NSEntityDescription *entity =
[[managedObjectModel entitiesByName] objectForKey:entityName];
NSManagedObject *newObject = [[NSManagedObject alloc]
initWithEntity:entity insertIntoManagedObjectContext:context];
return newObject;
注意上面的objectForKey!与NSDictionary相同
=============================================== ==================
dequeueReusableCellWithIdentifier并不总是返回非零对象。它用于重用现有的单元格。但是如果没有可用的,你需要分配/初始化一个。
类似的东西:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
if (cell == nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"];
}
// populate your cell
return cell;
}
答案 1 :(得分:1)
我认为您需要优化数据模型:
首先,从Language
到Video
的关系有一个复数名称videos
,这表示您希望每个Language
对象与许多Video
相关联对象。但是模型编辑器中的图像显示该关系目前被定义为" to-one"。
其次,为什么Language
实体上有一个language
实体,和一个Video
属性?从您的代码中,language
属性看起来像是表示语言名称的字符串。我建议你应该在name
实体上设置Language
属性。
除了这些要点之外,问题的关键还在于loadVideosTab
方法。它只创建一个Language
对象:
Language *language = [NSEntityDescription insertNewObjectForEntityForName:@"Language" inManagedObjectContext:context];
然后创建(或获取)多个Video
个对象,并与(一个)Language
建立关系,例如:
Video *chineseLanguage = (Video *)[Video videoLanguage:chinese inManagedObjectContext:context];
language.videos = chineseLanguage;
但是,由于videos
关系是" to-one",Language
对象只能与一个Video
相关联。因此,每次设置关系时,都会删除旧关系。在方法结束时,您的Language
只与Tamil Video
对象相关。其他七个Video
个对象与任何Language
无关。
每次运行都会创建一个新的Language
,并与泰米尔语Video
建立关系。因为每个Video
只能有一个Language
,所以当建立这种关系时,CoreData会将泰米尔视频的关系放到" old" Language
对象。因此,你最终得到了几个" old"与Language
无关的Video
个对象 - 这些是您的语言表视图中的空白行。
修改强>
关于处理多对多关系的注释:该关系表示为NSSet。如果您正在设置关系(并希望删除现有关系),则可以使用:
language.videos = [NSSet setWithObject:tamilLanguage];
或者要同时设置多个对象,请使用:
language.videos = [NSSet setWithArray:@[chineseLanguage, dutchLanguage, tamilLanguage]];
这些破坏了现有的关系,所以添加关系,同时保留现有关系,使用NSMutableSet
:
NSMutableSet *videos = [language mutableSetValueForKey:@"videos"];
[videos addObject:tamilLanguage];
(同样,removeObject
只删除其中一个相关对象)。我发现这些方法相当不直观,所以如果我有一个关系,其倒数是"到一个",我用它代替:
tamilLanguage.videoLanguage =语言;
话虽如此,我现在想知道这种反向关系是否也应该是“对多...”。 - Video
可以有多个Language
s?