I am trying to add new items to my collection view using insertItemsAtIndexPaths. My app crashes at performBatchupdate
- (void) addItems {
NSArray *newProducts = #[#"1",#"2",#"3",#"4"];
[self.collectionView performBatchUpdates:^{
NSMutableArray *arrayWithIndexPaths = [NSMutableArray array];
for (NSInteger index = self.array.count; index < (self.array.count + newProducts.count); index++) {
[arrayWithIndexPaths addObject:[NSIndexPath indexPathForRow:index inSection:0]];
}
[self.array addObjectsFromArray:newProducts];
[self.collectionView insertItemsAtIndexPaths:arrayWithIndexPaths];
}
completion:nil];
}
Following is the crash log:
* Assertion failure in -[UICollectionView _createPreparedCellForItemAtIndexPath:withLayoutAttributes:applyAttributes:]
This Assertion happens when cell is not registered with the collectionview. I am registering my cell.
This worked for me:
If Collection view is empty reload else insertItems.
- (void)addItems {
NSArray *newProducts = #[#"1",#"2",#"3",#"4"];
NSMutableArray *arrayWithIndexPaths = [NSMutableArray array];
for (NSInteger index = self.array.count; index < (self.array.count + newProducts.count); index++) {
[arrayWithIndexPaths addObject:[NSIndexPath indexPathForRow:index inSection:0]];
}
if (self.array) {
[self.array addObjectsFromArray:newProducts];
[self.collectionView performBatchUpdates:^{
[self.collectionView insertItemsAtIndexPaths:arrayWithIndexPaths];
}
completion:nil];
}
else {
self.array = [[NSMutableArray alloc] initWithArray:newProducts];
[self.collectionView reloadData];
}
}
Related
I'm working with a UICollection view and would like to animate changes to it's datasource. New datasource shares a lot of items with the old one, and I would like to animate insertion of new items and deletion of items that are no longer in the datasource, while keeping all other cells intact:
Current: [1,2,8,9]
Updated: [1,2,3,9]
Changes: insert 3, delete 8
Are there examples of swapping datasources for UICollectionView with animation?
I tried this, which causes the collection view to flash, but no animations are being played:
[self.collectionView performBatchUpdates:^{
[self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:0]];
} completion:^(BOOL finished) {
}];
This method complains about numbers of items not matching:
-(void)setDatasource:(NSMutableArray *)datasource
{
NSMutableArray* _datasourceBackup = _datasource;
//find out what is being added and deleted
NSMutableArray* itemsToDelete = [NSMutableArray arrayWithArray:_datasource];
DLog(#"delete raw: %lu", (unsigned long)itemsToDelete.count);
[itemsToDelete removeObjectsInArray:datasource];
DLog(#"delete filtered: %lu", (unsigned long)itemsToDelete.count);
NSMutableArray* itemsToAdd = [NSMutableArray arrayWithArray:datasource];
DLog(#"add raw: %lu", (unsigned long)itemsToAdd.count);
[itemsToAdd removeObjectsInArray:_datasource];
DLog(#"add filtered: %lu", (unsigned long)itemsToAdd.count);
//1 update datasource
_datasource = datasource;
if(_datasourceBackup.count == 0)
{
//original load
[self.collectionView reloadData];
}else
{
[self.collectionView performBatchUpdates:^{
//convert items to add/delete into index paths and give them to collection view to animate
NSMutableArray* indexPathsToDelete = [NSMutableArray array];
for(NSString* identifier in itemsToDelete)
{
NSInteger index = [_datasourceBackup indexOfObject:identifier];
[indexPathsToDelete addObject: [NSIndexPath indexPathForRow:index inSection:0]];
}
if(indexPathsToDelete.count > 0 )
{
[self.collectionView deleteItemsAtIndexPaths:indexPathsToDelete];
}
NSMutableArray* indexPathsToInsert = [NSMutableArray arrayWithCapacity:itemsToAdd.count];
for(NSString* identifier in itemsToAdd)
{
NSInteger index = [_datasource indexOfObject:identifier];
[indexPathsToInsert addObject: [NSIndexPath indexPathForRow:index inSection:0]];
}
if(indexPathsToInsert.count > 0 )
{
[self.collectionView insertItemsAtIndexPaths:indexPathsToInsert];
}
} completion:nil];
}
}
Good day! My default tableview sectionHeaderHeight is 56. I want to update it when sectionOpened method gets called.This is the following code:
- (void) sectionOpened : (NSInteger) section
{
sectionHeight = #"YES";
getsection = section;
NSLog(#"Section Clicked: %ld",(long)getsection);
SectionInfo *array = [self.sectionInfoArray objectAtIndex:section];
array.open = YES;
NSInteger count = [array.category.menulist count];
NSMutableArray *indexPathToInsert = [[NSMutableArray alloc] init];
for (NSInteger i = 0; i<count;i++)
{
[indexPathToInsert addObject:[NSIndexPath indexPathForRow:i inSection:section]];
}
NSMutableArray *indexPathsToDelete = [[NSMutableArray alloc] init];
NSInteger previousOpenIndex = self.openSectionIndex;
if (previousOpenIndex != NSNotFound)
{
SectionInfo *sectionArray = [self.sectionInfoArray objectAtIndex:previousOpenIndex];
sectionArray.open = NO;
NSInteger counts = [sectionArray.category.menulist count];
[sectionArray.sectionView toggleButtonPressed:FALSE];
for (NSInteger i = 0; i<counts; i++)
{
[indexPathsToDelete addObject:[NSIndexPath indexPathForRow:i inSection:previousOpenIndex]];
}
}
UITableViewRowAnimation insertAnimation;
UITableViewRowAnimation deleteAnimation;
if (previousOpenIndex == NSNotFound || section < previousOpenIndex)
{
insertAnimation = UITableViewRowAnimationTop;
deleteAnimation = UITableViewRowAnimationBottom;
}
else
{
insertAnimation = UITableViewRowAnimationBottom;
deleteAnimation = UITableViewRowAnimationTop;
}
[menulistTable beginUpdates];
if(section==3 || section==4 || section==5) // this section has cells like submenu of a menu
{
menulistTable.sectionHeaderHeight = 20.0;
[menulistTable reloadSections:[NSIndexSet indexSetWithIndex:section] withRowAnimation:UITableViewRowAnimationNone];
//[menulistTable reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationNone];
}
[menulistTable insertRowsAtIndexPaths:indexPathToInsert withRowAnimation:insertAnimation];
[menulistTable deleteRowsAtIndexPaths:indexPathsToDelete withRowAnimation:deleteAnimation];
[menulistTable endUpdates];
self.openSectionIndex = section;
}
But nothing happening. I also used indexSetWithIndex:0. Whats wrong in the code?
Try like this to change Height:
[menulistTable beginUpdates];
menulistTable.sectionHeaderHeight = 20.0;
[menulistTable endUpdates];
Edited:
Take variable to set SectionHeaderHeight. Then place this code:
[menulistTable beginUpdates];
secHeaderHeight = 20.0;
[menulistTable endUpdates];
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
return secHeaderHeight;
}
Hope it will work for you.
I've a UITableView with custom UITableViewCell in a Master-Detail structure. The cells of the table can be documents or folder...if a cell is a document, it opens the document in the detail view, but if is a folder it opens other cells below.
This works perfectly on iOS 7, but running in iOS 8, when I tap a cell, my app freezes and it takes more and more memory...at the end it crashes.
I've tried EVERYTHING...I've searched EVERYWHERE...nothing seems to work!!!
Here is my didSelectRowAtIndexPath: method
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"OBJECTS: %lu", (unsigned long)_objects.count);
[tableView deselectRowAtIndexPath:indexPath animated:YES];
NSDictionary *d=[_objects objectAtIndex:indexPath.row];
if([d valueForKey:#"Objects"])
{
NSArray *ar=[d valueForKey:#"Objects"];
BOOL isAlreadyInserted=NO;
for(NSDictionary *dInner in ar )
{
NSInteger index=[_objects indexOfObjectIdenticalTo:dInner];
isAlreadyInserted=(index>0 && index!=NSIntegerMax);
if(isAlreadyInserted) break;
}
if(isAlreadyInserted)
{
[self miniMizeThisRows:ar];
}
else
{
NSUInteger count=indexPath.row+1;
NSMutableArray *arCells=[NSMutableArray array];
for(NSDictionary *dInner in ar )
{
[arCells addObject:[NSIndexPath indexPathForRow:count inSection:0]];
[_objects insertObject:dInner atIndex:count++];
}
[self addRowsAtIndexPaths:arCells];
}
}
else
{
NSDictionary *object = [_objects objectAtIndex:indexPath.row];
self.detailViewController.detailItem = [object objectForKey:#"name"];
self.detailViewController.title = [object objectForKey:#"displayedName"];
if(![cache containsObject:object])
{
TableCustomCell *cell = (TableCustomCell*)[tableView cellForRowAtIndexPath:indexPath];
[cell.marker setHidden:NO];
[cache addObject:object];
}
}
}
And in addRowsAtIndexPaths: I just do
- (void)addRowsAtIndexPaths:(NSArray*)indexPaths
{
[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationMiddle];
[self.tableView endUpdates];
}
Someone helps???
Thank you.
EDIT
I figured out that the cycle is caused by my UITableViewCell sublcass...
I used this code to manage the indentation:
- (void)layoutSubviews
{
[super layoutSubviews];
float indentPoints = self.indentationLevel * self.indentationWidth;
self.contentView.frame = CGRectMake(indentPoints,
self.contentView.frame.origin.y,
self.contentView.frame.size.width - indentPoints,
self.contentView.frame.size.height);
}
By commenting this, the app works...but the cells aren't indented!
Try to press 'Pause' button at debugger, during this freeze, and look on callStack, than, press 'Continue' button, and again 'Pause', and look for calls, witch of them is the same. It looks like call cycle.
I am inserting and removing rows at random from a tableview. However, if the rows I want to remove are not in view, they are not actually removed in the UI when the given row does come back to view.
If I add and remove the rows only on visible rows, it works just fine. What could be causing this problem?
- (void)sectionHeaderView:(BaseSectionHeaderView *)sectionHeaderView sectionOpened:(NSInteger)sectionOpened {
LogInfo(#"opening section:%ld",sectionOpened);
[_dataSource setSectionAtIndex:sectionOpened Open:YES];
/*
Create an array containing the index paths of the rows to insert: These correspond to the rows for each quotation in the current section.
*/
NSInteger countOfRowsToInsert = [_dataSource tableView:_tableView numberOfRowsInSection:sectionOpened];
NSMutableArray *indexPathsToInsert = [[NSMutableArray alloc] init];
for (NSInteger i = 0; i < countOfRowsToInsert; i++) {
[indexPathsToInsert addObject:[NSIndexPath indexPathForRow:i inSection:sectionOpened]];
}
/*
Create an array containing the index paths of the rows to delete: These correspond to the rows for each quotation in the previously-open section, if there was one.
*/
NSMutableArray *indexPathsToDelete = [[NSMutableArray alloc] init];
if (self.openSection.openSectionHeaderView != nil) {
[_openSection.openSectionHeaderView toggleOpenWithUserAction:NO];
// NSInteger countOfRowsToDelete = [_purchaseInvoiceListTable.tableView numberOfRowsInSection:self.openSection.openSectionIndex];
NSInteger countOfRowsToDelete = [_dataSource tableView:_tableView numberOfRowsInSection:self.openSection.openSectionIndex];
[_dataSource setSectionAtIndex:self.openSection.openSectionIndex Open:NO];
for (NSInteger i = 0; i < countOfRowsToDelete; i++) {
[indexPathsToDelete addObject:[NSIndexPath indexPathForRow:i inSection:self.openSection.openSectionIndex]];
}
}
// style the animation so that there's a smooth flow in either direction
UITableViewRowAnimation insertAnimation;
UITableViewRowAnimation deleteAnimation;
if (self.openSection.openSectionHeaderView == nil || sectionOpened < self.openSection.openSectionIndex) {
insertAnimation = UITableViewRowAnimationTop;
deleteAnimation = UITableViewRowAnimationBottom;
}
else {
insertAnimation = UITableViewRowAnimationBottom;
deleteAnimation = UITableViewRowAnimationTop;
}
// apply the updates
[_tableView beginUpdates];
[_tableView insertRowsAtIndexPaths:indexPathsToInsert withRowAnimation:insertAnimation];
[_tableView deleteRowsAtIndexPaths:indexPathsToDelete withRowAnimation:deleteAnimation];
[_tableView endUpdates];
self.openSection.openSectionIndex = sectionOpened;
self.openSection.openSectionHeaderView = sectionHeaderView;
self.openSection.sectionHeight = [NSNumber numberWithFloat:sectionHeaderView.frame.size.height];
LogInfo(#"sectionOpened:%ld",sectionOpened);
}
i think you should reload the table view by this...
[tableView reloadData];
and if from this it will not give the expected result then remove the object from array that fill the table view's row. i think this will definitely work...
tell me is it working or not...
I have the following code.
NSMutableArray *aList = [[NSMutableArray alloc] init];
for (NSIndexPath *indexPath in tableview1.indexPathsForSelectedRows) {
NSString *r = [NSString stringWithFormat:#"%i",indexPath.row];
[aList addObject:r];
}
So what I get is a list of UITableView rows that are selected. Is there a simple way of getting a list of rows that are NOT selected?
Thank you for your help.
There may be elegant solutions but this will work.
NSMutableArray *allIndexPaths = [#[]mutableCopy];
NSInteger nSections = [self.tableView numberOfSections];
for (int j=0; j<nSections; j++) {
NSInteger nRows = [self.tableView numberOfRowsInSection:j];
for (int i=0; i<nRows; i++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:j];
[allIndexPaths addObject:indexPath];
}
}
NSArray *selectedIndexPaths = self.tableView.indexPathsForSelectedRows;
for (NSIndexPath *indexPath in selectedIndexPaths) {
[allIndexPaths removeObject:indexPath];
}
NSArray *unselectedIndexPaths = [NSArray arrayWithArray:allIndexPaths];
EDIT : As per Rikkles suggestion
NSMutableArray *unselectedIndexPaths = [#[]mutableCopy];
NSArray *selectedIndexPaths = self.tableView.indexPathsForSelectedRows;
NSInteger nSections = [self.tableView numberOfSections];
for (int j=0; j<nSections; j++) {
NSInteger nRows = [self.tableView numberOfRowsInSection:j];
for (int i=0; i<nRows; i++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:j];
if (![selectedIndexPaths containsObject:indexPath]) {
[unselectedIndexPaths addObject:indexPath];
}
}
}
NSMutableArray *a = [NSMutableArray array]; // that's all unselected indexPaths
NSArray *l = [self.tableView indexPathsForSelectedRows];
NSIndexPath *idx;
for (NSUInteger sec=0; sec<[self.tableView numberOfSections]; sec++) {
for (NSUInteger r=0; r<[self.tableView numberOfRowsInSection:sec]; r++) {
idx = [NSIndexPath indexPathForRow:r inSection:sec];
if(![l containsObject:idx]){
[a addObject:idx];
}
}
}
You can use the numberOfRows return value minus the total selected rows.
I've added several additional lines to Anupdas' code. He certainly deserves the entire credit.
NSMutableArray *allIndexPaths = [#[]mutableCopy];
NSInteger nSections = [tableview1 numberOfSections];
for (int j=0; j < nSections; j++) {
NSInteger nRows = [tableview1 numberOfRowsInSection:j];
for (int i=0; i<nRows; i++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:j];
[allIndexPaths addObject:indexPath];
}
}
NSArray *selectedIndexPaths = tableview1.indexPathsForSelectedRows;
for (NSIndexPath *indexPath in selectedIndexPaths) {
[allIndexPaths removeObject:indexPath];
}
NSMutableArray *bList = [[NSMutableArray alloc] init];
for (NSInteger k = 0; k < allIndexPaths.count; k++) {
NSString *r = [NSString stringWithFormat:#"%i",[[allIndexPaths objectAtIndex:k] row]];
[bList addObject:r];
}
NSMutableArray * yourNewArr = [[NSMutableArray alloc ] init];
for(NSUInteger i = 0 ; i < [yourArr count] ; i++){
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
NSArray *selIndexs = self.tableView.indexPathsForSelectedRows;
if(![selIndexs containsObject:indexPath]){
[yourNewArr addObject:indexPath];
}
}
NSLog(#"%#", yourNewArr); // this will contain unselected index paths.