I am stumped on this. Here is my scenario. In my Appdelegate, I am creating
- An instance of a view controller that will be presented modally to collect two pieces of data from the user
- A tableView controller
- A navigation controller that I init with the tableview controller as its rootview
controller - A collectionviewController, note that I am registering the class for the cell here as well.
- A UITabBarController that I add the navigation controller to as the first view and the
collectionview to as the second view.. The TabBarCOntorller is set as the rootview of the window.
Here is the code:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:
(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.evc = [[WTAEntryControllerViewController alloc] init];
WTATableView *tv = [[WTATableView alloc] initWithNibName:@"WTATableView" bundle:nil];
UINavigationController *nav = [[UINavigationController alloc]
initWithRootViewController:tv];
nav.tabBarItem.title = NSLocalizedString(@"Birthday List", nil);
nav.tabBarItem.image = [UIImage imageNamed:@"birthdaycake"];
WTACollectionViewController *cv = [[WTACollectionViewController alloc]
initWithCollectionViewLayout:[[UICollectionViewFlowLayout alloc] init]];
[cv.collectionView registerClass:[UICollectionViewCell class]
forCellWithReuseIdentifier:CellIdentifier];
cv.collectionView.backgroundColor = [UIColor whiteColor];
NSLog(@"cv instance %@", cv);
UITabBarController *tabController = [[UITabBarController alloc] init];
tabController.viewControllers = @[nav, cv];
self.window.rootViewController = tabController;
[self.window makeKeyAndVisible];
return YES
}
The tableview has a navbar button that presents the model view controller. The modal view controller has a button that takes the values from a textField and a datepicker and posts that to the default notification center with the values in the UserInfo dictionary. The tableView contorller subscribes to the notification, puts the UserInfo dictionary in a MutableArray and updates the table. That works fine.
The problem is with the CollectionVIew. The collectionVIew receives the notification and calls a target method to handle the notification. I am trying to take the UserInfo dictionary from the notification and add a new cell to the collectionView.
BUT...
When the collectionview receives the notification I get the error:
'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).'
Here is the code for the CollectionView:
#import "WTACollectionViewController.h"
#import "UIColor+WTAExtensions.h"
#import "WTAAppDelegate.h"
#import "WTAEntryControllerViewController.h"
@interface WTACollectionViewController () <UICollectionViewDelegateFlowLayout>
@property (strong, nonatomic) NSMutableArray *birthdays;
@end
@implementation WTACollectionViewController
-(id)initWithCollectionViewLayout:(UICollectionViewLayout *)layout{
NSLog(@"Calling init on Collection");
if(!(self = [super initWithCollectionViewLayout:layout])){
return nil;
}
self.birthdays = [NSMutableArray array];
NSLog(@"Count of Birthdays at init %ld", (long)[self.birthdays count] );
self.title = NSLocalizedString(@"Birthday Grid", nil);
self.tabBarItem.image = [UIImage imageNamed:@"package"];
NSLog(@"cv instance getting notif %@", self);
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(handleNotification:)
name:@"BirthdayAdded"
object:nil];
return self;
}
-(void)handleNotification:(NSNotification *)notification
{
[self.birthdays addObject:[notification userInfo]];
NSLog(@"Count of birthdays when the notification is received: %ld", (long)
[self.birthdays count]);
[self.collectionView insertItemsAtIndexPaths:@[[NSIndexPath indexPathForItem:
(self.birthdays.count -1) inSection:0]]];
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(@"Calling View Did Load on Collection");
}
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
return 1;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:
(NSInteger)section{
NSLog(@"Count of birthdays in the number of items in section: %ld", (long)
[self.birthdays count]);
return [self.birthdays count];
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
cellForItemAtIndexPath:(NSIndexPath *)indexPath {
NSLog(@"Calling the cell for index path method");
NSDictionary *dict = [[NSMutableDictionary alloc] init];
dict = self.birthdays[indexPath.item];
UICollectionViewCell *cell = [collectionView
dequeueReusableCellWithReuseIdentifier:CellIdentifier forIndexPath:indexPath];
NSAssert(cell != nil, @"Expected a Cell");
cell.contentView.backgroundColor = [UIColor randomColor];
return cell;
}
#define GAP (1.0f)
-(CGSize)collectionView:(UICollectionView *)collectionView layout:
(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath
*)indexPath {
CGFloat edgeLength = self.collectionView.frame.size.width / 4.0f - GAP;
return (CGSize) {.width = edgeLength, .height = edgeLength};
}
-(CGFloat)collectionView:(UICollectionView *)collectionView layout:
(UICollectionViewLayout *)collectionViewLayout minimumLineSpacingForSectionAtIndex:
(NSInteger)section{
return GAP;
}
-(CGFloat)collectionView:(UICollectionView *)collectionView layout:
(UICollectionViewLayout *)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:
(NSInteger)section{
return GAP;
}
@end
I would so much appreciate anyone who can point anything out here.....
I had the same error when I've inserted elements in a collectionView with only one section.
I've done the Timothy fix, but it was not enough. In fact, the collectionView had already refreshed its data when I tried to insert new elements. Using collectionView numberOfItemsInSection solves the problem.
It's a bug with using
insertItemsAtIndexPaths
on an emptyUICollectionView
. Just do this:(Can't believe this is still broken in iOS8.)
In my case,
numberOfItemsInSection
was being called twice fromself.performBatchUpdates
(once for the BEFORE number, once for the AFTER number) because I was trying to insert something into the collectionView before the view controller had finished loading.The solution is to add
guard isViewLoaded else { return }
before callingself.performBatchUpdates
.As for me the issue was connected to the Index Set - where to insert sections. 1. I've inserted Sections 0..<10 (10 sections) 2. I was trying to insert Sections to IndexSet 9..<19. But appending the new objects to the end of the dataSource array. Correct Index set should be (10..<20)
Thus the last section was still expecting the number of rows from Section 9. But in data source - it was at index 19.
collectionView?.insertSections(IndexSet(integersIn: startIndex..<(startIndex + series.count)))
It is a bug in UICollectionView even in iOS 11, but not exist in UITableView, very easy to reproduce: Assume the following SWIFT code is working:
Change it to:
It will crash...
UICollectionView will lose track of the original number of items in some condition, just add a dummy line before insertItems command will avoid this kind of crash:
Hope it could help in your situation.