4

UICollectionView:我做错了。我只是不知道怎么做。

我的设置

我在装有 iOS 6.0.1 的 iPhone 4S 上运行它。

我的目标

我有一个表格视图,其中一个部分专门用于图像:表格视图部分的屏幕截图

当用户点击“添加图片...”单元格时,系统会提示他们从照片库中选择一张图片或使用相机拍摄一张新图片。该应用程序的那部分似乎工作正常。

当用户第一次添加图像时,“无图像”标签将从第二个表格单元格中删除,并在其位置添加以编程方式创建的 UICollectionView。那部分似乎也工作正常。

集合视图应该显示他们添加的图像,这就是我遇到麻烦的地方。(我知道随着图像数量的增加,我将不得不跳过一些障碍来让表格视图单元格自行放大。我还没有那么远。)

当我尝试将项目插入集合视图时,它会引发异常。稍后再谈。

我的代码

我有负责表视图的 UITableViewController 也充当集合视图的委托和数据源。这是相关代码(我省略了我认为与此问题无关的控制器位,包括方法中的行,例如-viewDidLoad。我也省略了大部分图像采集代码,因为我认为它不相关):

#define ATImageThumbnailMaxDimension 100

@interface ATAddEditActivityViewController ()
{
    UICollectionView* imageDisplayView;
    NSMutableArray* imageViews;
}

@property (weak, nonatomic) IBOutlet UITableViewCell *imageDisplayCell;
@property (weak, nonatomic) IBOutlet UILabel *noImagesLabel;
@end

@implementation ATAddEditActivityViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    UICollectionViewFlowLayout* flowLayout = [[UICollectionViewFlowLayout alloc] init];
    flowLayout.scrollDirection = UICollectionViewScrollDirectionVertical;

    imageDisplayView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 0, 290, 120) collectionViewLayout:flowLayout];  // The frame rect still needs tweaking
    imageDisplayView.delegate = self;
    imageDisplayView.dataSource = self;
    imageDisplayView.backgroundColor = [UIColor yellowColor];  // Just so I can see the actual extent of the view
    imageDisplayView.opaque = YES;
    [imageDisplayView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"Cell"];

    imageViews = [NSMutableArray array];
}

#pragma mark - UIImagePickerDelegate
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
    /* ...code defining imageToSave omitted... */

    [self addImage:imageToSave toCollectionView:imageDisplayView];


    [self dismissModalViewControllerAnimated:YES];
}

#pragma mark - UICollectionViewDelegate
- (BOOL)collectionView:(UICollectionView *)collectionView shouldShowMenuForItemAtIndexPath:(NSIndexPath *)indexPath
{
    return YES;
}

#pragma mark - UICollectionViewDatasource
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    UICollectionViewCell* cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath];
    [[cell  contentView] addSubview:imageViews[indexPath.row]];
    return cell;
}

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    return [imageViews count];
}

#pragma mark - UICollectionViewDelegateFlowLayout
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
    return ((UIImageView*)imageViews[indexPath.item]).bounds.size;
}

#pragma mark - Image Handling
- (void)addImage:(UIImage*)image toCollectionView:(UICollectionView*)cv
{
    if ([imageViews count] == 0)  {
        [self.noImagesLabel removeFromSuperview];
        [self.imageDisplayCell.contentView addSubview:cv];
    }

    UIImageView* imageView = [[UIImageView alloc] initWithImage:image];
    /* ...code that sets the bounds of the image view omitted... */

    [imageViews addObject:imageView];
    [cv insertItemsAtIndexPaths:@[[NSIndexPath indexPathForItem:[imageViews count]-1 inSection:0]]];
    [cv reloadData];
}

@end

总结一下:

  • 集合视图在-viewDidLoad方法中实例化和配置
  • 接收所选图像调用的 UIImagePickerDelegate 方法-addImage:toCollectionView
  • ...创建一个新的图像视图来保存图像并将其添加到数据源数组和集合视图中。这是产生异常的行。
  • UICollectionView 数据源方法依赖于 imageViews 数组。

例外

由于未捕获的异常“NSInternalInconsistencyException”而终止应用程序,原因:“无效更新:第 0 节中的项目数无效。更新 (1) 后现有节中包含的项目数必须等于该节中包含的项目数更新前的部分 (1),加上或减去从该部分插入或删除的项目数(1 插入,0 删除),加上或减去移入或移出该部分的项目数(0 移入,0 移动出去)。'

如果我解析正确,这告诉我的是(全新的!)集合视图认为它是用单个项目创建的。所以,我添加了一个日志-addImage:toCollectionView来测试这个理论:

NSLog(@"%d", [cv numberOfItemsInSection:0]);

有了那条线,就永远不会抛出异常!调用-numberOfItemsInSection:必须强制集合视图查询其数据源并意识到它没有项目。或者其他的东西。我在这里推测。但是,好吧,无论如何:此时集合视图仍然不显示任何项目,所以我仍然做错了什么,我不知道是什么。

综上所述

  1. 当我尝试将项目添加到新创建和插入的集合视图时,我遇到了一个奇怪的异常......除非我-numberOfItemsInSection:在尝试插入之前调用。
  2. 即使我设法通过一个阴暗的解决方法来克服异常,这些项目仍然不会显示在集合视图中。

抱歉小说的问题。有任何想法吗?

4

4 回答 4

11

不幸的是,接受的答案是不正确的(尽管它在正确的轨道上);问题是您在应该刚刚插入项目时调用了 reloadData 和 insertItems。所以而不是:

[imageViews addObject:imageView];
[cv insertItemsAtIndexPaths:@[[NSIndexPath indexPathForItem:[imageViews count]-1 inSection:0]]];
[cv reloadData];

做就是了:

[imageViews addObject:imageView];
[cv insertItemsAtIndexPaths:@[[NSIndexPath indexPathForItem:[imageViews count]-1 inSection:0]]];

这不仅会给你一个很好的动画,它可以防止你低效地使用 tableview(在 1-cell 集合视图中不是什么大问题,但对于更大的数据集来说是一个大问题),并避免像你看到的那样崩溃,其中两种方法都试图修改集合视图(其中一种——reloadData——与其他方法不兼容)。

顺便说一句,reloadData 对 UICollectionView 不是很友好;如果您确实有一个相当大的和/或复杂的集合,并且在调用 reloadData 之前或之后不久发生了插入,则插入可能会在 reloadData 完成之前完成——这将可靠地导致“无效的项目数”崩溃(同样如此用于删除)。调用 reloadSections 而不仅仅是 reloadData 似乎有助于避免这个问题。

于 2013-07-22T23:42:49.637 回答
0

面临同样的问题,但我的原因是我忘记将集合视图数据源连接到视图控制器

于 2016-08-19T16:05:50.097 回答
0

这是因为 [单元格计数] 不等于 [实际索引计数] + [插入索引]。有时 dispatch_async 不包括数组插入数据和 insertItemsAtIndexPaths 的块。我遇到了有时崩溃的问题。它不会导致每次崩溃。

于 2017-10-15T19:19:30.207 回答
-1

只是一个猜测:在您插入第一张图像时,集合视图可能尚未加载其数据。但是,在异常消息中,集合视图声称“知道”插入之前的项目数 (1)。因此,它可能会延迟加载其数据insertItemsAtIndexPaths:并将结果作为“之前”状态。此外,您不需要在插入后重新加载数据。

长话短说,移动

[cv reloadData];

最多得到

if ([imageViews count] == 0)  {
    [self.noImagesLabel removeFromSuperview];
    [self.imageDisplayCell.contentView addSubview:cv];
    [cv reloadData];
}
于 2012-11-15T15:14:16.283 回答