【问题标题】:Invalid update: invalid number of items on UICollectionView无效更新:UICollectionView 上的项目数无效
【发布时间】:2013-10-12 13:25:31
【问题描述】:

我对此感到困惑。这是我的场景。在我的 Appdelegate 中,我正在创建

  1. 视图控制器的一个实例,将以模态方式呈现以从用户那里收集两条数据
  2. tableView 控制器
  3. 我使用 tableview 控制器作为其根视图初始化的导航控制器
    控制器
  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

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

【问题讨论】:

标签: ios objective-c uitableview uicollectionview


【解决方案1】:

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

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

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

【讨论】:

  • 这是一个救生员!谢谢!
  • 这在 iOS 8 中修复了吗?
  • 它适用于 iOS 8.1,在空的 collectionview 中插入项目。
  • 哇,我花了 30 多个小时来追踪这个问题。你有苹果错误报告的链接吗?我想在我的代码库中记录它。
  • 我需要在哪种方法中添加?这行代码?
【解决方案2】:

即使在 iOS 11 中也是 UICollectionView 的一个 bug,但在 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)])

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

【讨论】:

  • 有趣的是,Apple 知道这一点吗?
  • 我没有向 Apple 报告。
  • 我仍然有这个错误(2018 年 5 月),您的解决方案已修复它。谢谢你。当它准备好并加载时,我在 UICollectionView 中动态插入 NativeAd 作为单元格,但有时它会崩溃。 “虚拟线”似乎有效。
【解决方案3】:

当我在只有一个部分的 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];

【讨论】:

    【解决方案4】:

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

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

    【讨论】:

    • 什么是“isViewLoaded”
    • @TàTruhoada 这是 UIViewController 上的布尔值,在 UI 元素显示在屏幕上后变为真。 developer.apple.com/documentation/uikit/uiviewcontroller/…
    • 我在 ViewDidLoad 中设置 dataSource 时发生了崩溃。在我读完这篇文章后,我把它移到查看确实出现了,并且没有再次崩溃
    • @p-sun:根据 Apple 的说法,isViewLoaded 是:一个布尔值,表示视图当前是否加载到内存中。这意味着视图加载到内存之前变得可见。如果你需要检查 viewController 是否可见,你应该做viewController.viewIfLoaded?.window != nil
    【解决方案5】:

    对我而言,问题与索引集有关 - 在哪里插入部分。 1. 我已插入第 0..

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

    collectionView?.insertSections(IndexSet(integersIn: startIndex..

    【讨论】:

      【解决方案6】:

      我没有练习 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 移出)。'

      【讨论】:

        【解决方案7】:
        1. 由于collectionView是异步重新加载数据,调用[collectionView reloadData]方法时,dataSource可能会在一段时间后重新加载。您可以调用 [collectionView layoutIfNeeded] 来强制 collectionView 立即重新加载。
        2. 当您在 [collectionView reloadData] 上调用 [collectionView insertItemsAtIndexPaths:@[indexPaths...]] 时,有时项目数可能不相等,您可以在调用 [collectionView insertItemsAtIndexPaths 之前调用 [collectionView numberOfItemsInSection:] 作为技巧:@[indexPaths...]]。
        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) {}];
        
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2020-12-17
          • 1970-01-01
          • 1970-01-01
          • 2021-03-11
          • 1970-01-01
          • 2015-07-01
          • 2020-03-04
          相关资源
          最近更新 更多