I am working on an UICollectionView based app, and I load it by -
NSUInteger newNumCells = [self.imageArray count];
NSIndexPath* newIndexPath;
NSMutableArray *indexPaths = [[NSMutableArray alloc] init];
[indexPaths removeAllObjects];
for (int i = 0; i < newNumCells; ++i) {
newIndexPath = [NSIndexPath indexPathForItem:i inSection:0];
[indexPaths addObject:newIndexPath];
}
self.indexPaths = indexPaths;
[self.collectionView insertItemsAtIndexPaths:indexPaths];
[self.collectionView reloadData];
It's working well.
Then I have another search function at same page, so when I get new search result from server, the self.imageArray content changed, I want to refresh current UICollectionView, for example, remove all items, and insert new items from current self.imageArray, but when I do delete items, always crash -
[self.collectionView performBatchUpdates:^{
//self.indexPaths = [self.collectionView indexPathsForVisibleItems];
//[self.imageArray removeAllObjects];
self.indexPaths = [self.collectionView indexPathsForVisibleItems];
[self.imageArray removeAllObjects];
[self.collectionView deleteItemsAtIndexPaths:self.indexPaths];
[self.collectionView.collectionViewLayout invalidateLayout];
//[self.collectionView reloadData];
//[self.collectionView deleteItemsAtIndexPaths:[self.collectionView indexPathsForVisibleItems]];
} completion:nil];
Crash info -
Assertion failure in -[UICollectionViewData validateLayoutInRect:], /SourceCache/UIKit_Sim/UIKit-2903.2/UICollectionViewData.m:341
2014-03-07 11:47:48.450 pixcell8[9089:a0b] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UICollectionView recieved layout attributes for a cell with an index path that does not exist: <NSIndexPath: 0xb4a8430> {length = 2, path = 0 - 0}'
So I want to ask how to do refresh the UICollectionView with new data? Thanks.
Update : this bug is because I have used a custom layout, and it had some wrong logics, and now it's fixed.
loading a collection view is very similar to a table view. Use the delegate and dataSource protocols for handling most of this.
for you case something like
self.collectionView.dataSource = self;
then in dataSource methods
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
return self.imageArray.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
//Set up and return your cell
}
When you want to change content you can replace or update contents of imageArray and reloadData.
New content from API comes in
[self.imageArray removeAllObjects];
[self.imageArray addObjectsFromArray:apiResponseArray];
[self.collectionView reloadData];
There are other approaches that maybe a bit more efficient but this should get you started. You can use the other methods for insert and batch as needed once the basic set up is done correctly.
Related
I'm facing this error:
Assertion failure in -[UITableView _endCellAnimationsWithContext:]
When I'm trying to use insertRowsAtIndexPaths:withRowAnimation:
Basically I have a NSMutableArray with objects, call this self.objects.
I'm adding objects to it like so:
MyObject *something = [MyObject new];
[self.objects addObject:something];
NSInteger count = self.objects.count;
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:count];
[self.tableView insertRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
A thing to note is that I'm using section rather than row to achieve cell spacing.
Update
Error message:
*** Assertion failure in -[UITableView _endCellAnimationsWithContext:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit/UIKit-3512.30.14/UITableView.m:1704
data source methods:
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [self.objects count];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 1;
}
Code Which will work:
MyObject *something = [MyObject new];
[self.objects addObject:something];
NSInteger count = self.objects.count;
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:count-1]
withRowAnimation:UITableViewRowAnimationAutomatic];
Description:
In your table view implementation you are using one row for reach section. And you are trying to insert row to the section which in not available(created).
So you have to first insert the section that only you can insert rows(in your implementation you are having one row per section so only insert the section).
It will be
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:count-1];
I have used UICollection view to show items in grid layout.
For data source I have use 5*5 dimensional array.
And also I am returning 5 for numberOfItems in section and 5 for numberOfSections.
Then also my app is getting crashed with following error:
'UICollectionView recieved layout attributes for a cell with an index path that does not exist: {length = 2, path = 0 - 5}'
//////===viewcontroller.m==///////
- (void)viewDidLoad {
self.theData = #[#[#"1",#"2",#"3",#"4",#"5"], #[#"6",#"7",#"8",#"9",#"10"],#[#"11",#"12",#"13",#"14",#"15"],#[#"16",#"17",#"18",#"19",#"20"],#[#"21",#"22",#"23",#"24",#"25"]];
MultpleLineLayout *layout = [[MultpleLineLayout alloc] init];
self.collectionView.collectionViewLayout = layout;
self.collectionView.showsHorizontalScrollIndicator = NO;
self.collectionView.showsVerticalScrollIndicator = NO;
layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
self.view.backgroundColor = [UIColor blackColor];
[self.collectionView registerClass:[DataCell class] forCellWithReuseIdentifier:#"DataCell"];
[self.collectionView reloadData];
}
- (NSInteger)collectionView:(UICollectionView *)view numberOfItemsInSection:(NSInteger)section
{
return 5;
}
- (NSInteger)numberOfSectionsInCollectionView: (UICollectionView *)collectionView {
return 5;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
DataCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"DataCell" forIndexPath:indexPath];
cell.label.text = self.theData[indexPath.section ][indexPath.row];
return cell;
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
// UICollectionViewCell *item = [collectionView cellForItemAtIndexPath:indexPath];
NSLog(#"%#",indexPath);
}
///////////////////////
Can anyone solve this problem?
Thanks in advance.
The MultipleLineLayout was originally written for infinite scrolling, so there was a problem with that implementation for your use. It should look like this,
-(NSArray*)layoutAttributesForElementsInRect:(CGRect)rect {
NSMutableArray* attributes = [NSMutableArray array];
for(NSInteger i=0 ; i < self.collectionView.numberOfSections; i++) {
for (NSInteger j=0 ; j < [self.collectionView numberOfItemsInSection:i]; j++) {
NSIndexPath* indexPath = [NSIndexPath indexPathForItem:j inSection:i];
[attributes addObject:[self layoutAttributesForItemAtIndexPath:indexPath]];
}
}
return attributes;
}
Just in case others find this question with Google - I received the same error with a very interesting index path:
'NSInternalInconsistencyException', reason: 'UICollectionView received
layout attributes for a cell with an index path that does not exist:
{length = 2, path = 0 - 0}'
I had simply forgotten to connect the collection view's data source and delegate to the view controller in Interface Builder. D'oh!
It works for me.
collectionView.reloadData()
collectionView.collectionViewLayout.invalidateLayout()
It comes because of the cache of the Cell's Autolayout when reloadData.
Hi I'm working with a Custom UICollectionView (https://github.com/SureCase/WaterfallCollectionView) where everything works fine. Now I'm setting up delete items from the UICollectionView, and I can delete them fine. The problem comes when I'm trying to delete the last item of the section.
It gives the following error.
*** Assertion failure in -[UICollectionViewData layoutAttributesForSupplementaryElementOfKind:atIndexPath:], /SourceCache/UIKit/UIKit-2935.137/UICollectionViewData.m:787
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'no UICollectionViewLayoutAttributes instance for -layoutAttributesForSupplementaryElementOfKind: UICollectionElementKindSectionHeader at path <NSIndexPath: 0xc000000000000016> {length = 2, path = 0 - 0}'
The Code I'm using to delete items is the following:
- (void) alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex == 0){
NSLog(#"Erasing Objects!");
//First I remove Items from DataSource, and after from Collection View
[self deleteItemsFromDataSourceAtIndexPaths:selectedIndexes];
[self.cv deleteItemsAtIndexPaths:selectedIndexes];
[selectedIObjectsIndexes removeAllObjects];
[selectedIndexes removeAllObjects];
EditUICollection=NO;
EasyMediaGreenView.hidden = YES;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.7 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[self.cv reloadData];
});
}else{
NSLog(#"Not delete");
}
}
So first I'm removing items from DataSource and then from de Collection View. Here the Code for removing from Data Source.
-(void)deleteItemsFromDataSourceAtIndexPaths:(NSArray *)itemPaths
{
NSMutableIndexSet *indexSet = [NSMutableIndexSet indexSet];
for (NSIndexPath *itemPath in itemPaths) {
[indexSet addIndex:itemPath.row];
}
[idObject removeObjectsAtIndexes:indexSet];
[typeObject removeObjectsAtIndexes:indexSet];
[urlObject removeObjectsAtIndexes:indexSet];
[textObject removeObjectsAtIndexes:indexSet];
}
Why is this happening when I try to remove last object, when all other objects are removed correctly? I would like to understand this part, & any idea how to fix this? Thanks to all.
I Found a solution!
I was always returning 1 for section, so even with no items the CollectionView was being constructed, and asking to build a Header, like this:
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView
viewForSupplementaryElementOfKind:(NSString *)kind
atIndexPath:(NSIndexPath *)indexPath; {
So I counted the items in my data array, and if the array is empty, there shouldn't be a section.
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
if(idObject.count>0){ return 1; }
else{ return 0; }
}
So where I'm deleting the items from my data array, I can check if the array is empty, if is not empty I'll delete items, if it is, I'll delete the entire section.
NSLog(#"Erasing Objects!");
[self deleteItemsFromDataSourceAtIndexPaths:selectedIndexes];
if(idShadeInside.count > 0){
[self.cv deleteItemsAtIndexPaths:selectedIndexes];
}
else{
//Creating an IndexSet with the Section to Erase
NSMutableIndexSet *indexSet = [NSMutableIndexSet indexSet];[indexSet addIndex:0];
[self.cv deleteSections:indexSet];
}
So problem fixed!
i have done this features, here i provides you a simple code to delete cell from Collection View.
Just pass you index in integer here.
-(void)remove:(int)i {
#try {
[self.collection performBatchUpdates:^{
[self.arrayThumbImg removeObjectAtIndex:i];
NSIndexPath *indexPath =[NSIndexPath indexPathForRow:i inSection:0];
[self.collection deleteItemsAtIndexPaths:[NSArray arrayWithObject:indexPath]];
} completion:^(BOOL finished) {
}];
}
#catch (NSException *exception) {
NSLog(#"Error: %#",exception);
}
#finally {
}
}
I am making a UICollectionView control which would look like (fig-1) :
I have added the ability to delete cell by swiping the cells to right.
My problem case - If I delete the last cell by swiping (fig-2) , which will call the following code.
- (void)removeTheCell:(AnyObject *)obj {
// remove the object
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:[self.allObjects indexOfObject:obj] inSection:0];
[self.allObjects removeObjectAtIndex:indexPath.row];
[self.collectionView deleteItemsAtIndexPaths:#[indexPath]];
}
And then add a new cell with different color using following method (fig-4):
- (void)addNewObject:(NSNotification *)notification {
NSDictionary *dict = notification.userInfo;
NSArray *newObjects_a = [dict objectForKey:ALL_OBJECTS];
NSMutableArray *indexArrays = [[NSMutableArray alloc] init];
for (AnyObject *obj in newObjects_a) {
[self.allObjects addObject:obj];
[indexArrays addObject:[NSIndexPath indexPathForItem:[self.allObjects indexOfObject:obj] inSection:0]];
}
[self.collectionView performBatchUpdates:^{
[self.collectionView insertItemsAtIndexPaths:indexArrays];
} completion:nil];
}
The cell that is displayed still looks like the old deleted cell with its last state (fig-4). But i checked the data source it doesn't contain the deleted object. It contains the latest data.
(fig-5)If i change to list layout by selecting the segment control which call the following method:
- (IBAction)SwitchCellFrames:(id)sender {
int selection = ((UISegmentedControl *)sender).selectedSegmentIndex;
isGridView = selection == 0 ? YES : NO;
if (isGridView) {
[self.collectionView setCollectionViewLayout:gridFlowLayout animated:YES];
}else {
[self.collectionView setCollectionViewLayout:listFlowLayout animated:YES];
}
}
layout variables are defined as :
gridFlowLayout = [[UICollectionViewFlowLayout alloc] init];
[gridFlowLayout setItemSize:CGSizeMake(160, 155)];
[gridFlowLayout setMinimumInteritemSpacing:0.0f];
[gridFlowLayout setMinimumLineSpacing:0.0f];
[gridFlowLayout setScrollDirection:UICollectionViewScrollDirectionVertical];
listFlowLayout = [[UICollectionViewFlowLayout alloc] init];
[listFlowLayout setItemSize:CGSizeMake(320, 80)];
[listFlowLayout setMinimumInteritemSpacing:0.0f];
[listFlowLayout setMinimumLineSpacing:0.0f];
[listFlowLayout setScrollDirection:UICollectionViewScrollDirectionVertical];
The collectionView now updates the new cell with the right color (fig-5/fig-6).
I tried [self.collectionView setNeedsDisplay] / [self.collectionView setNeedsLayout] / [self.collectionView reloadData]. These are not causing the UI to redraw itself.
I don't know what is causing the UICollectionView to retain the deleted view. Please Help.
Found a work arround that is working for me in this situation :
I was creating and updating the ui under - (void)awakeFromNib() of my customCell class. So, when the the new cell is added at the location from where a cell was earlier deleted, - (void)awakeFromNib() was not getting called again and a previous copy of cell was being returned.
Therefore, i made the ui update method public and removed it from - (void)awakeFromNib(). Calling UI update method explicitly from cellForItemAtIndexPath solved the problem for me.
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
CustomViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"CollectionCell" forIndexPath:indexPath];
cell.obj = [self.allObjects objectAtIndex:indexPath.row];
[cell handleChangesForLayoutAndPosition];
[cell updateUI];
[cell resetScrollView];
cell.delegate = self;
return cell;
}
So, I'm playing around with iOS programming a little bit. Don't got a lot of time, but I've been postponing to learn it for too long now. But I got stuck for 2 days now with this freaking problem. I'm getting the following error when I try to add cells to my collectionView.
'NSInternalInconsistencyException', reason: 'attempt to insert item 0 into section 0, but there are only 0 items in section 0 after the update'
Here's my code:
#interface DocumentsViewController() {
FileManager *fileManager;
NSMutableArray *folderContent;
}
#end
#implementation DocumentsViewController
#synthesize fileList;
- (void)viewDidLoad
{
[super viewDidLoad];
folderContent = [[NSMutableArray alloc] init];
fileManager = [[FileManager alloc] init];
NSArray *items = [fileManager getDirectoryInfo];
[self createFileList:items];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)createFileList:(NSArray *)items
{
for(NSString *item in items)
{
NSInteger count = [folderContent count];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:count inSection:0];
[folderContent insertObject:item atIndex:count];
[self.fileList insertItemsAtIndexPaths:#[indexPath]];
}
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
FilesViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"DefaultCell" forIndexPath:indexPath];
cell.image.image = [UIImage imageNamed:#"folder.png"];
cell.label.text = #"oi"; //objects[indexPath.row];
return cell;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return folderContent.count;
}
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return 1;
}
#end
Thanks in advance,
Lucas
UPDATE
Ok, so after a few VERY helpful tips, I got another error message:
'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of items in section 0. The number of items contained in an existing section after the update (1) must be equal to the number of items contained in that section before the update (1), plus or minus the number of items inserted or deleted from that section (1 inserted, 0 deleted) and plus or minus the number of items moved into or out of that section (0 moved in, 0 moved out).'
Like I said below, I might be using (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section wrong cause it's been called twice in a row, without giving time for the value of folderContent.count to change.
Do you really want to use insertItemsAtIndexPath when you create your list? This will cause them to animate in one at a time. Why not do a [self.fileList reloadData] after it is completely populated after the for loop?
In addition I think this line may be giving you issues -
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:count inSection:0];
You are using a collection view which doesn't have rows in the same sense as UITableViews (note the indexPathForItem instead of indexPathForRow)
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:count inSection:0];