Answers:
// 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];
}
[self.collectionView removeObserver:self forKeyPath:@"contentSize" context:NULL];
到-viewWillDisappear:animated:
为好。
这对我有用:
[self.collectionView reloadData];
[self.collectionView performBatchUpdates:^{}
completion:^(BOOL finished) {
/// collection-view finished reload
}];
Swift 4语法:
self.collectionView.reloadData()
self.collectionView.performBatchUpdates(nil, completion: {
(result) in
// ready
})
reloadData
刷新了collectionView
它,因此它再次使用了它的数据源方法... performBatchUpdates
附加了一个完成块,因此,如果两者都在主线程上执行,则您知道替换的任何代码都/// collection-view finished reload
将重新执行并进行布局collectionView
实际上非常简单。
例如,当您调用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
});
工作原理:
主线程(我们应该在其中进行所有UI更新)包含主队列,该主队列本质上是串行的,即它以FIFO方式工作。因此,在上面的示例中,第一个块被调用,其中reloadData
调用了我们的方法,第二个块中随后调用了其他任何东西。
现在,主线程也正在阻塞。因此,如果您reloadData
需要3秒钟来执行,则第二个块的处理将被这3秒钟推迟。
reloadData
现在,将单元的加载放入主线程中(即使从主线程调用也是如此)。因此,返回cellForRowAtIndexPath:
后实际上并未加载单元格(未调用)reloadData
。包装要在单元格加载后执行的代码,dispatch_async(dispatch_get_main...
并在调用后对其进行调用reloadData
将获得所需的结果。
只是为了添加一个很棒的@dezinezync答案:
迅捷3+
collectionView.collectionViewLayout.invalidateLayout() // or reloadData()
DispatchQueue.main.async {
// your stuff here executing after collectionView has been layouted
}
像这样做:
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)
})
尝试在reloadData()调用之后立即通过layoutIfNeeded()强制进行同步布局传递。似乎适用于iOS 12上的UICollectionView和UITableView。
collectionView.reloadData()
collectionView.layoutIfNeeded()
// cellForItem/sizeForItem calls should be complete
completion?()
作为dezinezync回答,你需要的是派遣到主队列后的代码块reloadData
由一个UITableView
或UICollectionView
,然后这个块将细胞后执行离队
为了使使用时更直观,我将使用如下扩展名:
extension UICollectionView {
func reloadData(_ completion: @escaping () -> Void) {
reloadData()
DispatchQueue.main.async { completion() }
}
}
它也可以实现UITableView
为
使用RxSwift / RxCocoa的另一种方法:
collectionView.rx.observe(CGSize.self, "contentSize")
.subscribe(onNext: { size in
print(size as Any)
})
.disposed(by: disposeBag)
performBatchUpdates
在我的情况下不起作用,这个做到了。干杯!
我使用了以下方法:在收集视图加载到用户可见之前,需要对所有可见单元格执行一些操作:
public func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
if shouldPerformBatch {
self.collectionView.performBatchUpdates(nil) { completed in
self.modifyVisibleCells()
}
}
}
请注意,在浏览集合视图时将调用此方法,因此,为了避免这种开销,我添加了:
private var souldPerformAction: Bool = true
在动作本身中:
private func modifyVisibleCells() {
if self.shouldPerformAction {
// perform action
...
...
}
self.shouldPerformAction = false
}
该动作仍将执行多次,以初始状态下的可见单元数为准。但在所有这些调用中,您将拥有相同数量的可见单元格(全部)。用户开始与集合视图进行交互之后,布尔标志将阻止其再次运行。
重新加载集合视图后,我只是执行以下操作来执行任何操作。您甚至可以在API响应中使用此代码。
self.collectionView.reloadData()
DispatchQueue.main.async {
// Do Task after collection view is reloaded
}
Def这样做:
//Subclass UICollectionView
class MyCollectionView: UICollectionView {
//Store a completion block as a property
var completion: (() -> Void)?
//Make a custom funciton to reload data with a completion handle
func reloadData(completion: @escaping() -> Void) {
//Set the completion handle to the stored property
self.completion = completion
//Call super
super.reloadData()
}
//Override layoutSubviews
override func layoutSubviews() {
//Call super
super.layoutSubviews()
//Call the completion
self.completion?()
//Set the completion to nil so it is reset and doesn't keep gettign called
self.completion = nil
}
}
然后像这样在VC中调用
let collection = MyCollectionView()
self.collection.reloadData(completion: {
})
确保您正在使用子类!
这项工作对我来说:
- (void)viewDidLoad {
[super viewDidLoad];
int ScrollToIndex = 4;
[self.UICollectionView performBatchUpdates:^{}
completion:^(BOOL finished) {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:ScrollToIndex inSection:0];
[self.UICollectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:NO];
}];
}
这就是我解决Swift 3.0问题的方式:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if !self.collectionView.visibleCells.isEmpty {
// stuff
}
}
试试这个:
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return _Items.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell;
//Some cell stuff here...
if(indexPath.row == _Items.count-1){
//THIS IS THE LAST CELL, SO TABLE IS LOADED! DO STUFF!
}
return cell;
}
你可以这样...
- (void)reloadMyCollectionView{
[myCollectionView reload];
[self performSelector:@selector(myStuff) withObject:nil afterDelay:0.0];
}
- (void)myStuff{
// Do your stuff here. This will method will get called once your collection view get loaded.
}