105

UICollectionViewが完全にロードされたときはいつでも、何らかの操作を行う必要があります。つまり、その時点で、UICollectionViewのすべてのデータソース/レイアウトメソッドを呼び出す必要があります。どうすればそれを知ることができますか?UICollectionViewのロードステータスを知るためのデリゲートメソッドはありますか?

4

21 に答える 21

169

これは私のために働いた:

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

Swift 4構文:

collectionView.reloadData()
collectionView.performBatchUpdates(nil, completion: {
    (result) in
    // ready
})
于 2015-06-05T14:02:06.327 に答える
63
// 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];
}
于 2014-10-24T02:02:24.483 に答える
30

それは実際にはかなり単純です。

たとえば、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秒によって延期されます。

于 2013-12-19T13:39:34.860 に答える
13

素晴らしい@dezinezyncの答えに追加するだけです:

Swift 3+

collectionView.collectionViewLayout.invalidateLayout() // or reloadData()
DispatchQueue.main.async {
    // your stuff here executing after collectionView has been layouted
}
于 2017-09-22T18:43:43.997 に答える
7

RxSwift / RxCocoaを使用した別のアプローチ:

        collectionView.rx.observe(CGSize.self, "contentSize")
            .subscribe(onNext: { size in
                print(size as Any)
            })
            .disposed(by: disposeBag)
于 2019-01-02T09:28:27.750 に答える
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)
        })
于 2016-09-10T11:03:52.967 に答える
4

dezinezyncが答えたように、必要なのはreloadDataUITableViewまたはの後にコードのブロックをメインキューにディスパッチすることですUICollectionView。その後、このブロックは、セルのキューから外れた後に実行されます。

使用時にこれをよりまっすぐにするために、次のような拡張機能を使用します。

extension UICollectionView {
    func reloadData(_ completion: @escaping () -> Void) {
        reloadData()
        DispatchQueue.main.async { completion() }
    }
}

また、に実装することができUITableViewます

于 2018-11-02T22:34:02.623 に答える
4

reloadData()呼び出しの直後にlayoutIfNeeded()を介して同期レイアウトパスを強制してみてください。iOS12のUICollectionViewとUITableViewの両方で機能するようです。

collectionView.reloadData()
collectionView.layoutIfNeeded() 

// cellForItem/sizeForItem calls should be complete
completion?()
于 2019-03-01T22:31:28.507 に答える
3

コレクションビューがリロードされた後、何かを実行するために次のことを実行しました。このコードは、API応答でも使用できます。

self.collectionView.reloadData()

DispatchQueue.main.async {
   // Do Task after collection view is reloaded                
}
于 2020-02-04T09:53:32.953 に答える
3

私がこれまでに見つけた最善の解決策は、CATransaction完了を処理するために使用することです。

スウィフト5

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

collectionView.reloadData()

CATransaction.commit()

更新:上記の解決策は、場合によっては機能するように見えますが、機能しない場合もあります。私は受け入れられた答えを使用することになりました、そしてそれは間違いなく最も安定して証明された方法です。Swift5バージョンは次のとおりです。

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

collectionView.reloadData()
于 2020-07-16T07:32:45.237 に答える
3

SWIFT 5

override func viewDidLoad() {
    super.viewDidLoad()
    
    // "collectionViewDidLoad" for transitioning from product's cartView to it's cell in that view
    self.collectionView?.addObserver(self, forKeyPath: "contentSize", options: NSKeyValueObservingOptions.new, context: nil)
}

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    if let observedObject = object as? UICollectionView, observedObject == self.collectionView {
        print("collectionViewDidLoad")
        self.collectionView?.removeObserver(self, forKeyPath: "contentSize")
    }
}
于 2020-12-04T11:55:18.883 に答える
1

これは私のために働きます:

__weak typeof(self) wself= self;
[self.contentCollectionView performBatchUpdates:^{
    [wself.contentCollectionView reloadData];
} completion:^(BOOL finished) {
    [wself pageViewCurrentIndexDidChanged:self.contentCollectionView];
}];
于 2016-01-12T03:18:06.797 に答える
1

コレクションビューがユーザーに表示される前に読み込まれたときに、表示されているすべてのセルに対して何らかのアクションを実行する必要がありました。次のように使用しました。

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
}

初期状態で表示されているセルの数として、アクションは引き続き複数回実行されます。ただし、これらすべての呼び出しで、同じ数の表示セル(すべて)が表示されます。また、ブールフラグは、ユーザーがコレクションビューを操作し始めた後、再び実行されないようにします。

于 2018-05-06T13:15:00.950 に答える
1

バッチ更新内でcollectionViewをリロードし、ブール値の「finish」を使用して完了ブロックをチェックインするだけです。

self.collectionView.performBatchUpdates({
        self.collectionView.reloadData()
    }) { (finish) in
        if finish{
            // Do your stuff here!
        }
    }
于 2019-12-06T18:54:54.783 に答える
0

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: {

})

サブクラスを使用していることを確認してください!!

于 2018-01-17T23:02:37.123 に答える
0

私のためのこの仕事:


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

}

于 2019-01-23T08:16:19.993 に答える
0

以下は私のために働いた唯一のアプローチです。

extension UICollectionView {
    func reloadData(_ completion: (() -> Void)? = nil) {
        reloadData()
        guard let completion = completion else { return }
        layoutIfNeeded()
        completion()
    }
}
于 2020-08-16T07:07:55.307 に答える
0

ここでのソリューションのほとんどは、の紛らわしい非同期性のために、信頼性がないか、非決定論的な動作(ランダムなバグを引き起こす可能性があります)を持っていますUICollectionView

UICollectionView信頼できる解決策は、の最後に完了ブロックを実行するようにサブクラス化することですlayoutSubviews()

Objectice-Cのコード: https ://stackoverflow.com/a/39648633

Swiftのコード: https ://stackoverflow.com/a/39798079

于 2021-06-09T09:08:38.313 に答える
-1

これが私がSwift3.0の問題を解決した方法です:

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    if !self.collectionView.visibleCells.isEmpty {
        // stuff
    }
}
于 2017-02-16T10:04:41.913 に答える
-9

これを試して:

- (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;
}
于 2013-11-07T04:43:41.133 に答える
-10

あなたはこのようにすることができます...

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

    }
于 2013-04-09T07:19:01.647 に答える