从核心数据加载 UICollectionView
Posted
技术标签:
【中文标题】从核心数据加载 UICollectionView【英文标题】:load UICollectionView from core data 【发布时间】:2013-07-25 21:54:38 【问题描述】:我正在开发一个 ios 应用程序,并在某个时间点将用户绘制的图像存储在 Core Data 中。 现在我想在 UICollectionView 中加载所有记录的图像,以便用户可以选择一个并在社交网络上分享。 除了最后一部分之外,我的应用程序中的所有内容都有效。 我在网上学习了各种教程,但我在 UICollectionView 上找到的所有示例都使用了来自 Flickr 或类似网站的图片。
这里是我的代码:
#import "LibraryViewController.h" (The name of the class we're in)
#import "SocialViewController.h"
#import "CollectionViewCell.h"
static NSString *cellIdentifier = @"MemeCell";
@implementation LibraryViewController
NSMutableArray *_objectChanges;
NSMutableArray *_sectionChanges;
#pragma mark - View Controller LifeCycle
- (void)awakeFromNib
[super awakeFromNib];
- (void)viewDidLoad
[super viewDidLoad];
_objectChanges = [NSMutableArray array];
_sectionChanges = [NSMutableArray array];
self.title = @"Meme collection";
- (void)didReceiveMemoryWarning
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
if ([[segue identifier] isEqualToString:@"share"])
NSIndexPath *indexPath = [[self.collectionView indexPathsForSelectedItems] lastObject];
NSManagedObject *object = [[self fetchedResultsController] objectAtIndexPath:indexPath];
SocialViewController *destViewController = segue.destinationViewController;
[destViewController setDetailItem:object];
#pragma mark - UICollectionView
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
id <NSFetchedResultsSectionInfo> sectionInfo = [self.fetchedResultsController sections][section];
return [sectionInfo numberOfObjects];
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
CollectionViewCell *cell = (CollectionViewCell *)[collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier
forIndexPath:indexPath];
NSManagedObject *object = [self.fetchedResultsController objectAtIndexPath:indexPath];
[cell setImage:[UIImage imageWithData:[object valueForKey:@"image"]]];
return cell;
#pragma mark - Fetched results controller
- (NSFetchedResultsController *)fetchedResultsController
if (_fetchedResultsController != nil)
return _fetchedResultsController;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Meme" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setFetchBatchSize:20];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:@"Master"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error])
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
return _fetchedResultsController;
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
NSMutableDictionary *change = [NSMutableDictionary new];
switch(type)
case NSFetchedResultsChangeInsert:
change[@(type)] = @(sectionIndex);
break;
case NSFetchedResultsChangeDelete:
change[@(type)] = @(sectionIndex);
break;
[_sectionChanges addObject:change];
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath
NSMutableDictionary *change = [NSMutableDictionary new];
switch(type)
case NSFetchedResultsChangeInsert:
change[@(type)] = newIndexPath;
break;
case NSFetchedResultsChangeDelete:
change[@(type)] = indexPath;
break;
case NSFetchedResultsChangeUpdate:
change[@(type)] = indexPath;
break;
case NSFetchedResultsChangeMove:
change[@(type)] = @[indexPath, newIndexPath];
break;
[_objectChanges addObject:change];
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
if ([_sectionChanges count] > 0)
[self.collectionView performBatchUpdates:^
for (NSDictionary *change in _sectionChanges)
[change enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, id obj, BOOL *stop)
NSFetchedResultsChangeType type = [key unsignedIntegerValue];
switch (type)
case NSFetchedResultsChangeInsert:
[self.collectionView insertSections:[NSIndexSet indexSetWithIndex:[obj unsignedIntegerValue]]];
break;
case NSFetchedResultsChangeDelete:
[self.collectionView deleteSections:[NSIndexSet indexSetWithIndex:[obj unsignedIntegerValue]]];
break;
case NSFetchedResultsChangeUpdate:
[self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:[obj unsignedIntegerValue]]];
break;
];
completion:nil];
if ([_objectChanges count] > 0 && [_sectionChanges count] == 0)
[self.collectionView performBatchUpdates:^
for (NSDictionary *change in _objectChanges)
[change enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, id obj, BOOL *stop)
NSFetchedResultsChangeType type = [key unsignedIntegerValue];
switch (type)
case NSFetchedResultsChangeInsert:
[self.collectionView insertItemsAtIndexPaths:@[obj]];
break;
case NSFetchedResultsChangeDelete:
[self.collectionView deleteItemsAtIndexPaths:@[obj]];
break;
case NSFetchedResultsChangeUpdate:
[self.collectionView reloadItemsAtIndexPaths:@[obj]];
break;
case NSFetchedResultsChangeMove:
[self.collectionView moveItemAtIndexPath:obj[0] toIndexPath:obj[1]];
break;
];
completion:nil];
[_sectionChanges removeAllObjects];
[_objectChanges removeAllObjects];
@end
当我运行并尝试访问此 ViewController 时,应用程序崩溃并显示以下错误消息: 2013-07-25 23:08:11.832 MemeGen[87259:c07] * 由于未捕获的异常“NSInvalidArgumentException”而终止应用程序,原因:“+entityForName:nil 不是搜索实体名称“Meme”的合法 NSManagedObjectContext 参数' * 首先抛出调用栈:
它指向这行代码:
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Meme" inManagedObjectContext:self.managedObjectContext];
显然 self.managedObjectContext 为零。 我应该如何分配它?而当用户第一次启动应用,核心数据为空时,应该如何管理呢?只要没有图片就禁止访问?
否则,我知道如何将绘图图像存储在文件系统上。如果有人知道在 UICollectionView 中加载它的方法,它也可能是我可以接受的解决方案。
【问题讨论】:
【参考方案1】:除非您使用外部存储功能,否则我不建议将图像存储在核心数据中。大型二进制对象很少需要存储在核心数据之类的东西中。当然,您可以将文件系统路径或 URL 存储在指向文件系统上图像的核心数据对象上。
要将 Core Data 与 UICollectionView 一起使用,请查看 NSFetchedResultsController。 这是一个 example 一起使用这两者来帮助您入门。
【讨论】:
感谢您的回答。实际上我发现了我的代码中缺少的东西(但我不能在 8 个小时之前发布它:-))。如果我在核心数据中创建对象时选择外部存储,是否足够?否则,我会很高兴学习一种从应用程序的 Document 文件夹加载这些图像的方法......我会看看你提供的链接。非常感谢您的帮助! 我去看了您提供的链接,Ash Furrow 编写的文件属于我确实遵循的教程之一,这对我编写这门课很有帮助。 (是你吗?)再次感谢。【参考方案2】:您将希望获得对 ManagedObjectContext 的引用。通常这是在您的应用程序委托中创建的。在这种情况下,你会想要这样的东西 -
NSManagedObjectContext *aManagedObjectContext = ((AppDelegate *)[UIApplication sharedApplication].delegate).managedObjectContext;
如果您愿意,可以在 viewDidLoad 中执行此操作并将其分配给 self.managedObjectContext。
【讨论】:
感谢您的帮助!我发现了丢失的内容,现在已加载存储的图像并且它不再崩溃......我需要在 fetchedResultsController 方法中添加这些行: NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"image" 升序:不]; NSArray *sortDescriptors = @[sortDescriptor]; 并添加这个方法: - (NSManagedObjectContext *)managedObjectContext NSManagedObjectContext *context = nil; id 委托 = [[UIApplication sharedApplication] 委托]; if ([delegate performSelector:@selector(managedObjectContext)]) context = [delegate managedObjectContext]; 返回上下文;以上是关于从核心数据加载 UICollectionView的主要内容,如果未能解决你的问题,请参考以下文章
加载主 TableViewController 后,值从核心数据中消失