37

我很难过。这是我的场景。在我的 Appdelegate 中,我正在创建

  1. 一个视图控制器的实例,它将以模态方式呈现以从用户那里收集两条数据
  2. 一个 tableView 控制器
  3. 我使用 tableview 控制器作为其 rootview 控制器初始化的导航
    控制器
  4. 一个collectionviewController,注意我在这里也注册了单元格的类。
  5. 我将导航控制器添加到的 UITabBarController 作为第一个视图,将
    collectionview 添加到第二个视图。TabBarCONtorller 设置为窗口的根视图。

这是代码:

 - (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
}

tableview 有一个导航栏按钮,用于显示模型视图控制器。模态视图控制器有一个按钮,该按钮从 textField 和 datepicker 中获取值,并将其与 UserInfo 字典中的值一起发布到默认通知中心。tableView 控制器订阅通知,将 UserInfo 字典放入 MutableArray 并更新表。这很好用。

问题出在 CollectionVIew 上。collectionVIew 接收通知并调用目标方法来处理通知。我正在尝试从通知中获取 UserInfo 字典并向 collectionView 添加一个新单元格。

但...

当 collectionview 收到通知时,我收到错误:

'无效更新:第0节中的项目数无效。更新后现有节中包含的项目数(1)必须等于更新前该节中包含的项目数(1),加上或减去从该部分插入或删除的项目数(1 插入,0 删除)加上或减去移入或移出该部分的项目数(0 移入,0 移出)。

下面是 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

我非常感谢任何能在这里指出任何事情的人......

4

8 回答 8

68

这是insertItemsAtIndexPaths在空上使用的错误UICollectionView。只需这样做:

if (self.birthdays.count == 1) {
    [self.collectionView reloadData];
} else {
    [self.collectionView insertItemsAtIndexPaths:@[[NSIndexPath indexPathForItem:   (self.birthdays.count -1) inSection:0]]];
}

(不敢相信这在 iOS8 中仍然被破坏。)

于 2013-10-05T21:31:41.613 回答
33

即使在 iOS 11 中也是 UICollectionView 中的错误,但在 UITableView 中不存在,很容易重现:假设以下 SWIFT 代码正在运行:

addItemInDataSource()
collectionView!.insertItems(at: [IndexPath(item: position, section: 0)])

将其更改为:

collectionView!.reloadData()    // <-- This new line will make UICollection crash...

addItemInDataSource()
collectionView!.insertItems(at: [IndexPath(item: position, section: 0)])

会崩溃...

UICollectionView 在某些情况下会丢失原始项目数,只需在 insertItems 命令之前添加一个虚拟行即可避免这种崩溃:

collectionView!.reloadData()

collectionView!.numberOfItems(inSection: 0) //<-- This code is no used, but it will let UICollectionView synchronize number of items, so it will not crash in following code.
addItemInDataSource()
collectionView!.insertItems(at: [IndexPath(item: position, section: 0)])

希望对您的情况有所帮助。

于 2017-10-15T03:35:42.570 回答
13

当我在只有一个部分的 collectionView 中插入元素时,我遇到了同样的错误。

我已经完成了 Timothy 修复,但这还不够。事实上,当我尝试插入新元素时,collectionView 已经刷新了它的数据。使用 collectionView numberOfItemsInSection 解决了这个问题。

[self.collectionView performBatchUpdates:^{
        NSMutableArray *arrayWithIndexPaths = [NSMutableArray array];
        for (NSUInteger i = [self.collectionView numberOfItemsInSection:0]; i < self.elements.count ; i++)
        {
            [arrayWithIndexPaths addObject:[NSIndexPath indexPathForRow:i inSection:0]];
        }

        [self.collectionView insertItemsAtIndexPaths:arrayWithIndexPaths];
    } completion:nil];
于 2015-07-02T12:37:28.640 回答
6

就我而言,numberOfItemsInSection被调用了两次self.performBatchUpdates(一次用于 BEFORE 编号,一次用于 AFTER 编号),因为我试图在视图控制器完成加载之前将某些内容插入到 collectionView 中。

解决方法是guard isViewLoaded else { return }在调用之前添加self.performBatchUpdates

于 2017-03-16T17:48:07.483 回答
0

至于我,这个问题与索引集有关——在哪里插入部分。1. 我已插入 Sections 0..<10(10 个部分) 2. 我试图将 Sections 插入 IndexSet 9..<19。但是将新对象附加到 dataSource 数组的末尾。正确的索引集应该是 (10..<20)

因此,最后一节仍然期望第 9 节的行数。但在数据源中 - 它位于索引 19 处。

collectionView?.insertSections(IndexSet(integersIn: startIndex..<(startIndex + series.count)))

于 2018-02-01T11:31:18.093 回答
0
  1. 由于collectionView是异步重新加载数据,调用[collectionView reloadData]方法时,dataSource可能会在一段时间后重新加载。您可以调用 [collectionView layoutIfNeeded] 来强制 collectionView 立即重新加载。
  2. 当您在 [collectionView reloadData] 上调用 [collectionView insertItemsAtIndexPaths:@[indexPaths...]] 时,有时项目数可能不相等,您可以在调用 [collectionView insertItemsAtIndexPaths:@[ 之前调用 [collectionView numberOfItemsInSection:] 作为技巧索引路径...]]。
  3. 当dataSource为空时,插入会崩溃,你应该调用[collectionView reloadData]而不是[collectionView numberOfItemsInSection:]

如上所述,为避免此问题,您可以这样做:

1. when reload collectionView:

    [self.collectionView reloadData];
    [self.collectionView layoutIfNeeded];

2. when invoke the batch updates:

    [self.collectionView performBatchUpdates:^{
        ...
        [self.dataSource addDataSource:moreDatas];

        NSMutableArray *addIndexPaths;
        ...

        if (self.dataSource.count == 1 ||
            [self.collectionView numberOfItemsInSection:0] == self.dataSource.count){
            [self.collectionView reloadData];
        }else{
            [self.collectionView insertItemsAtIndexPaths:addIndexPaths];
        }
    } completion:^(BOOL finished) {}];

于 2019-12-11T06:19:10.793 回答
0

在我的情况下,我正在加载视频,因此collectionView.reloadData()无法运行,我必须使用该insert方法将每个视频添加到 cv 的底部。

我遇到了一个奇怪的问题,我只有一个部分。当我在当前选项卡上并且视图可见时,以下代码运行良好:

datasource.append(post)

let lastIndex = datasource.count - 1
let indexPath = IndexPath(item: lastIndex, section: 0)

collectionView.performBatchUpdates({
    self.collectionView.insertItems(at: [indexPath])
})

但是,如果视图不再可见,因为我切换了 tabs,当上面的代码运行时(即使我在另一个选项卡上它仍然运行),我会得到Invalid update: invalid number of items on UICollectionView错误。

我在这里尝试了所有答案,但没有一个有效。甚至那些说要使用的cv.reloadData()。唯一有效的是当我按照nikita_gaydukov 的ray wenderlich 回答时。他说要做里面的一切performBatchUpdates。它起作用了,因为错误消失了!

collectionView.performBatchUpdates({

    self.datasource.append(post)

    let lastIndex = self.datasource.count - 1
    let indexPath = IndexPath(item: lastIndex, section: 0)

    self.collectionView.insertItems(at: [indexPath])
})
于 2022-03-04T05:00:20.330 回答
0

我没有练习 Obj-c,所以我用 Swift 写我的答案。

假设生日是一个日期数组作为字符串

birthdays = [String]()

func addDate() {
   birthdays.append("your_new_date")
   collectionView.insertItems(at: [IndexPath(item: birthdays.count - 1, section: 0)] ) 
}

func removeDate() {
   if birthdays.count > 0 {
      birthdays.removeLast() // use remove(at: Int) instead if u want
      collectionView.deleteItems(at: [IndexPath(item: birthdays.count, section: 0)] )
   }
}

你应该避免这个错误:

当 collectionview 收到通知时,我收到错误:

'无效更新:第0节中的项目数无效。更新后现有节中包含的项目数(1)必须等于更新前该节中包含的项目数(1),加上或减去从该部分插入或删除的项目数(1 插入,0 删除)加上或减去移入或移出该部分的项目数(0 移入,0 移出)。

于 2019-04-05T12:58:28.777 回答