我如何知道UICollectionView已完全加载?

Jir*_*une 90 delegates reload uicollectionview

每当完全加载UICollectionView时,我必须做一些操作,即那时应该调用所有UICollectionView的数据源/布局方法.我怎么知道?是否有任何委托方法来了解UICollectionView加载状态?

小智 135

这对我有用:

[self.collectionView reloadData];
[self.collectionView performBatchUpdates:^{}
                              completion:^(BOOL finished) {
                                  /// collection-view finished reload
                              }];
Run Code Online (Sandbox Code Playgroud)

Swift 4语法:

self.collectionView.reloadData()
self.collectionView.performBatchUpdates(nil, completion: {
    (result) in
    // ready
})
Run Code Online (Sandbox Code Playgroud)

  • 当collectionView具有动态大小的单元格时,对我不起作用 (8认同)
  • @ G.Abhisek Errrr,当然......你需要解释什么?第一行`reloadData`刷新`collectionView`,因此它再次利用它的数据源方法...`performBatchUpdates`附加了一个完成块,所以如果两个都在主线程上执行,你知道任何代码代替` /// collection-view finished reload`将使用新的布局`collectionView`执行 (5认同)
  • 在 iOS 9 上为我工作 (2认同)
  • 这种方法是完美的解决方案,没有任何头痛。 (2认同)
  • @ingaham 这对我来说非常适合动态大小的单元格,Swift 4.2 IOS 12 (2认同)

Cul*_*SUN 61

// In viewDidLoad
[self.collectionView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionOld context:NULL];

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary  *)change context:(void *)context
{
    // You will get here when the reloadData finished 
}

- (void)dealloc
{
    [self.collectionView removeObserver:self forKeyPath:@"contentSize" context:NULL];
}
Run Code Online (Sandbox Code Playgroud)

  • 滚动时内容大小也会发生变化,因此不可靠. (39认同)
  • @ericosg添加`[self.collectionView removeObserver:self forKeyPath:@"contentSize"context:NULL];`to`-viewWillDisappear:animated:`. (4认同)

dez*_*ync 25

它实际上非常简单.

例如,当您调用UICollectionView的reloadData方法或它的布局的invalidateLayout方法时,您将执行以下操作:

dispatch_async(dispatch_get_main_queue(), ^{
    [self.collectionView reloadData];
});

dispatch_async(dispatch_get_main_queue(), ^{
    //your stuff happens here
    //after the reloadData/invalidateLayout finishes executing
});
Run Code Online (Sandbox Code Playgroud)

为什么这样有效:

主线程(我们应该进行所有UI更新的地方)包含主队列,它本质上是串行的,即它以FIFO方式工作.所以在上面的例子中,调用了第一个块,reloadData调用了我们的方法,然后是第二个块中的任何其他块.

现在主线程也是阻塞的.因此,如果你reloadData需要3秒执行,第二个块的处理将被这些3推迟.

  • ```reloadData```现在将主要线程上的单元格加载排队(即使从主线程调用).所以在```reloadData```返回后,实际上并没有加载单元格(`````cellForRowAtIndexPath:`````).在你的单元格加载到```dispatch_async(dispatch_get_main ...```并在调用```reloadData```之后调用它时,你需要执行的代码包装将实现所需的结果. (6认同)
  • 我实际上只是测试过这个,在我所有其他委托方法之前调用该块.所以它不起作用? (2认同)

nik*_*ans 12

只是添加一个伟大的@dezinezync答案:

Swift 3+

collectionView.collectionViewLayout.invalidateLayout() // or reloadData()
DispatchQueue.main.async {
    // your stuff here executing after collectionView has been layouted
}
Run Code Online (Sandbox Code Playgroud)


Chi*_*yen 7

使用 RxSwift/RxCocoa 的另一种方法:

        collectionView.rx.observe(CGSize.self, "contentSize")
            .subscribe(onNext: { size in
                print(size as Any)
            })
            .disposed(by: disposeBag)
Run Code Online (Sandbox Code Playgroud)


Dar*_*rko 6

像这样做:

       UIView.animateWithDuration(0.0, animations: { [weak self] in
                guard let strongSelf = self else { return }

                strongSelf.collectionView.reloadData()

            }, completion: { [weak self] (finished) in
                guard let strongSelf = self else { return }

                // Do whatever is needed, reload is finished here
                // e.g. scrollToItemAtIndexPath
                let newIndexPath = NSIndexPath(forItem: 1, inSection: 0)
                strongSelf.collectionView.scrollToItemAtIndexPath(newIndexPath, atScrollPosition: UICollectionViewScrollPosition.Left, animated: false)
        })
Run Code Online (Sandbox Code Playgroud)


tyl*_*ler 5

尝试在 reloadData() 调用之后立即通过 layoutIfNeeded() 强制同步布局传递。似乎适用于 iOS 12 上的 UICollectionView 和 UITableView。

collectionView.reloadData()
collectionView.layoutIfNeeded() 

// cellForItem/sizeForItem calls should be complete
completion?()
Run Code Online (Sandbox Code Playgroud)


Max*_*dev 5

到目前为止我发现的最好的解决方案是使用CATransaction来处理完成。

斯威夫特 5

CATransaction.begin()
CATransaction.setCompletionBlock {
    // UICollectionView is ready
}

collectionView.reloadData()

CATransaction.commit()
Run Code Online (Sandbox Code Playgroud)

更新:上述解决方案似乎在某些情况下有效,而在某些情况下则无效。我最终使用了公认的答案,这绝对是最稳定且经过验证的方法。这是 Swift 5 版本:

private var contentSizeObservation: NSKeyValueObservation?
Run Code Online (Sandbox Code Playgroud)
contentSizeObservation = collectionView.observe(\.contentSize) { [weak self] _, _ in
      self?.contentSizeObservation = nil
      completion()
}

collectionView.reloadData()
Run Code Online (Sandbox Code Playgroud)