UICollectionViewが完全にロードされるたびに、つまりUICollectionViewのすべてのデータソース/レイアウトメソッドを呼び出す必要があるときに、何らかの操作を行う必要があります。どうやってそれを知っていますか?? UICollectionViewのロード状態を知るためのデリゲートメソッドはありますか?
// 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 reloadData];
[self.collectionView performBatchUpdates:^{}
completion:^(BOOL finished) {
/// collection-view finished reload
}];
Swift 4の構文:
self.collectionView.reloadData()
self.collectionView.performBatchUpdates(nil, completion: {
(result) in
// ready
})
実際にはかなり単純です。
たとえば、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
メソッドが呼び出され、その後に2番目のブロックに何かが続きます。
現在、メインスレッドもブロックしています。したがって、reloadData
の実行に3秒かかる場合、2番目のブロックの処理はそれらの3秒遅延されます。
素晴らしい@dezinezyncの答えに追加するだけです:
Swift 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)
})
RxSwift/RxCocoaを使用した別のアプローチ:
collectionView.rx.observe(CGSize.self, "contentSize")
.subscribe(onNext: { size in
print(size as Any)
})
.disposed(by: disposeBag)
ReloadData()呼び出しの直後に、layoutIfNeeded()を介して同期レイアウトパスを強制してください。 iOS 12のUICollectionViewとUITableViewの両方で動作するようです。
collectionView.reloadData()
collectionView.layoutIfNeeded()
// cellForItem/sizeForItem calls should be complete
completion?()
これは私のために働く:
__weak typeof(self) wself= self;
[self.contentCollectionView performBatchUpdates:^{
[wself.contentCollectionView reloadData];
} completion:^(BOOL finished) {
[wself pageViewCurrentIndexDidChanged:self.contentCollectionView];
}];
dezinezync と答えたので、必要なのは、reloadData
またはUITableView
からUICollectionView
の後のコードブロックをメインキューにディスパッチすることです。セルのデキュー後に実行される
使用時にこれをよりまっすぐにするために、次のような拡張機能を使用します。
extension UICollectionView {
func reloadData(_ completion: @escaping () -> Void) {
reloadData()
DispatchQueue.main.async { completion() }
}
}
UITableView
にも実装できます
私のためのこの仕事:
- (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];
}];
}
コレクションビューがユーザーに表示される前にロードされたときに、表示されているすべてのセルに対して何らかのアクションを実行する必要がありました。
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
}
アクションは、初期状態の可視セルの数として、引き続き複数回実行されます。ただし、これらの呼び出しのすべてで、同じ数の可視セル(すべて)があります。また、ブールフラグは、ユーザーがコレクションビューとの対話を開始した後、ブールフラグが再度実行されるのを防ぎます。
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: {
})
サブクラスを使用していることを確認してください!!