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
方法-addImage:toCollectionView
由于未捕获的异常'NSInternalInconsistencyException'而终止应用程序,原因:'无效更新:第0部分中的项目数无效。更新后的现有部分中包含的项目数(1)必须等于项目数在更新(1)之前包含在该部分中,加上或减去从该部分插入或删除的项目数(插入1个,删除0个)以及加入或减去移入或移出该部分的项目数量(0移入,0搬出去。'
如果我正在解析这个权利,这告诉我的是(全新的!)集合视图认为它是用单个项目创建的。所以,我在-addImage:toCollectionView
添加了一个日志来测试这个理论:
NSLog(@"%d", [cv numberOfItemsInSection:0]);
在那里有那条线,异常永远不会被抛出!对-numberOfItemsInSection:
的调用必须强制集合视图查询其数据源并意识到它没有项目。或者其他的东西。我猜想这里。但是,好吧,无论如何:集合视图在这一点上仍然没有显示任何项目,所以我仍然做错了什么,我不知道是什么。
-numberOfItemsInSection:
。对不起小说的问题。有什么想法吗?
答案 0 :(得分:11)
不幸的是,接受的答案是不正确的(虽然它在正确的轨道上);问题是你正在调用reloadData&您应该刚刚插入项目时插入项目。所以而不是:
[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单元集合视图中没什么大不了的,但是对于更大的数据集来说是一个很大的问题),并且避免像一个这样的崩溃你看到了,两个方法都试图修改集合视图(其中一个 - reloadData - 与其他方法不兼容)。
顺便说一句,reloadData不是非常友好的UICollectionView;如果你确实有一个相当大的& /或复杂的集合,并且在调用reloadData之前或之后很快就会发生插入,那么插入可能会在reloadData完成之前完成 - 这将可靠地导致"无效数量的项目&# 34;崩溃(同样适用于删除)。调用reloadSections而不仅仅是reloadData似乎有助于避免这个问题。
答案 1 :(得分:0)
只是猜测:当您插入第一个图像时,集合视图可能尚未加载其数据。但是,在异常消息中,集合视图声称“知道”插入前的项目数(1)。因此,它可能在insertItemsAtIndexPaths:
中延迟加载其数据并将结果视为“之前”状态。此外,插入后无需重新加载数据。
长话短说,移动
[cv reloadData];
最终获得
if ([imageViews count] == 0) {
[self.noImagesLabel removeFromSuperview];
[self.imageDisplayCell.contentView addSubview:cv];
[cv reloadData];
}
答案 2 :(得分:0)
面对同样的问题,但与我的原因是我忘了将集合视图数据源连接到视图控制器
答案 3 :(得分:0)
这是因为[单元格数]不等于[实际索引计数] + [插入索引]。 有时dispatch_async不包含数组插入数据块和insertItemsAtIndexPaths。 我遇到了somtimes崩溃的问题。每次崩溃都不是原因。