从核心数据加载 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的主要内容,如果未能解决你的问题,请参考以下文章

从核心数据到表的数据加载非常缓慢

如何将从核心数据加载的数据保存在数组中?

从核心数据加载 UICollectionView

加载主 TableViewController 后,值从核心数据中消失

通过核心数据加载文件后,NSBrowser 不会从 NSTreeController 重新填充子项

从大型 JSON 加载核心数据导致应用程序崩溃