UICollectionView'ün tamamen yüklendiğini nasıl bilebilirim?


98

UICollectionView tam olarak yüklendiğinde, yani o sırada tüm UICollectionView veri kaynağı / düzen yöntemleri çağrılmalıdır. Bunu nasıl bilebilirim?? UICollectionView yüklenme durumunu bilmek için herhangi bir temsilci yöntemi var mı?

Yanıtlar:


62
// 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];
}

1
Geri döndüğümde bu kilitleniyor, aksi halde iyi çalışıyor.
ericosg

4
@ericosg eklemek [self.collectionView removeObserver:self forKeyPath:@"contentSize" context:NULL];için -viewWillDisappear:animated:de.
pxpgraphics

Parlak! Çok teşekkür ederim!
Abdo

41
Kaydırma sırasında içerik boyutu da değişir, bu nedenle güvenilir değildir.
Ryan Heitner

Bir zaman uyumsuz dinlenme yöntemi kullanarak verilerini alan bir koleksiyon görünümü için denedim. Bu, bu yöntem tetiklendiğinde Görünür Hücrelerin her zaman boş olduğu anlamına gelir, yani başlangıçta koleksiyondaki bir öğeye kaydırırım.
Chucky

156

Bu benim için çalıştı:

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

Swift 4 sözdizimi:

self.collectionView.reloadData()
self.collectionView.performBatchUpdates(nil, completion: {
    (result) in
    // ready
})

1
Benim için iOS 9'da çalıştı
Magoo

5
@ G.Abhisek Errrr, elbette .... neyi açıklamaya ihtiyacın var? İlk satır reloadData, collectionViewveri kaynağı yöntemlerini tekrar kullanması için yeniler ... performBatchUpdateseklenmiş bir tamamlama bloğu vardır, bu nedenle her ikisi de ana iş parçacığı üzerinde gerçekleştirilirse, yerine yerleştirilen herhangi bir kodun /// collection-view finished reloadyeni ve düzenlenmiş bir şekilde yürütüleceğini bilirsinizcollectionView
Magoo

8
CollectionView dinamik boyutlu hücrelere
sahipken

2
Bu yöntem, baş ağrısız mükemmel bir çözümdür.
arjavlad

1
@ingaham Bu, dinamik boyutlu hücrelerle benim için mükemmel çalışıyor, Swift 4.2 IOS 12
Romulo BM

27

Aslında oldukça basit.

Örneğin, UICollectionView'ın reloadData yöntemini veya düzenininvalidateLayout yöntemini çağırdığınızda, aşağıdakileri yaparsınız:

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
});

Bu neden işe yarıyor:

Ana iş parçacığı (tüm UI güncellemelerini yapmamız gereken yer), doğası gereği seri olan ana kuyruğu barındırır, yani FIFO tarzında çalışır. Dolayısıyla, yukarıdaki örnekte, bizim reloadDatayöntemimiz çağrılan ilk blok , ardından ikinci bloktaki herhangi bir şey çağrılır.

Şimdi ana iş parçacığı da engelliyor. Yani reloadData, yürütmek için 3 saniye alırsanız , ikinci bloğun işlenmesi bu 3 saniye tarafından ertelenecektir.


2
Aslında bunu test ettim, diğer tüm temsilci yöntemlerimden önce blok çağrılıyor. Yani işe yaramıyor mu?
taylorcressy

@taylorcressy hey, kodu güncelledim, şu anda 2 üretim uygulamasında kullandığım bir şey. Ayrıca açıklama da eklenmiştir.
dezinezync

Benim için çalışmıyor. ReloadData'yı çağırdığımda, collectionView ikinci bloğun yürütülmesinden sonra hücre ister. Bu yüzden @taylorcressy'nin sahip olduğu aynı sorunu yaşıyorum.
Raphael

1
İkinci blok çağrısını birinciye koymayı deneyebilir misin? Doğrulanmamış, ancak işe yarayabilir.
dezinezync

6
reloadDataşimdi ana iş parçacığı üzerindeki hücrelerin yüklenmesini sıralar (ana iş parçacığından çağrılsa bile). Yani hücreler, dönüşlerden cellForRowAtIndexPath:sonra aslında yüklenmez ( çağrılmaz) reloadData. Hücreleriniz yüklendikten sonra çalıştırılmasını istediğiniz kodu sarmak dispatch_async(dispatch_get_main...ve aramanızdan sonra onu çağırmak reloadDataistenen sonucu elde edecektir.
Tylerc230

12

Sadece harika bir @dezinezync yanıtına eklemek için:

Swift 3+

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

@nikas bu görünümde eklememiz gereken belirdi !!
SARATH SASI

1
Mutlaka değil, düzen nihayet yüklendiğinde bir şeyler yapmak istediğiniz her yerden arayabilirsiniz.
nikans

Öğeleri ekledikten sonra collectionView kaydırmamı en sonunda yapmaya çalışırken parşömen titreşimleri alıyordum, bu sorunu çözdü, teşekkürler.
Skoua

6

Bunu şöyle yapın:

       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)
        })

4

ReloadData () çağrısından hemen sonra layoutIfNeeded () yoluyla senkronize bir düzen geçişini zorlamayı deneyin. İOS 12'de hem UICollectionView hem de UITableView için çalışıyor gibi görünüyor.

collectionView.reloadData()
collectionView.layoutIfNeeded() 

// cellForItem/sizeForItem calls should be complete
completion?()

Teşekkürler dostum! Uzun zamandır bu sorunun çözümünü arıyordum.
Trzy Gracje

3

Gibi dezinezync cevap, ne gerek sonra kod bloğu ana sıraya sevk etmektir reloadDatabir gelen UITableViewveya UICollectionView, ve sonra bu blok hücrelerinin sonra infaz edilecek dequeuing

Bunu kullanırken daha düzgün hale getirmek için, şöyle bir uzantı kullanırım:

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

Ayrıca için uygulanabilir UITableViewyanı


3

RxSwift / RxCocoa kullanarak farklı bir yaklaşım:

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

1
Ben bunu sevdim. performBatchUpdatesbenim durumumda çalışmıyordu, bu yapıyor. Şerefe!
Zoltán

@ MichałZiobro, kodunuzu bir yerde güncelleyebilir misiniz? Yöntem yalnızca contentSize değiştirildiğinde çağrılmalıdır? Yani, her kaydırmada değiştirmediğiniz sürece, bu çalışmalıdır!
Chinh Nguyen

1

Bu benim için çalışıyor:

__weak typeof(self) wself= self;
[self.contentCollectionView performBatchUpdates:^{
    [wself.contentCollectionView reloadData];
} completion:^(BOOL finished) {
    [wself pageViewCurrentIndexDidChanged:self.contentCollectionView];
}];

2
saçma ... bu hiçbir şekilde planlamayı bitirmedi.
Magoo

1

Koleksiyon görünümü kullanıcıya görünür olmadan önce yüklendiğinde tüm görünür hücrelerde bazı işlemlerin yapılmasına ihtiyacım vardı, kullandım:

public func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
    if shouldPerformBatch {
        self.collectionView.performBatchUpdates(nil) { completed in
            self.modifyVisibleCells()
        }
    }
}

Koleksiyon görünümünde gezinirken bunun çağrılacağına dikkat edin, bu yüzden bu ek yükü önlemek için ekledim:

private var souldPerformAction: Bool = true

ve eylemin kendisinde:

private func modifyVisibleCells() {
    if self.shouldPerformAction {
        // perform action
        ...
        ...
    }
    self.shouldPerformAction = false
}

Eylem, ilk durumda görünen hücrelerin sayısı kadar birden çok kez gerçekleştirilecektir. ancak tüm bu aramalarda, aynı sayıda görünür hücreye sahip olacaksınız (tümü). Ve boole bayrağı, kullanıcı koleksiyon görünümüyle etkileşim kurmaya başladıktan sonra tekrar çalışmasını engelleyecektir.


1

Koleksiyon görünümü yeniden yüklendikten sonra herhangi bir şey gerçekleştirmek için aşağıdakileri yaptım. Bu kodu API yanıtında bile kullanabilirsiniz.

self.collectionView.reloadData()

DispatchQueue.main.async {
   // Do Task after collection view is reloaded                
}

1

Şimdiye kadar bulduğum en iyi çözüm, CATransactiontamamlamanın üstesinden gelmek için kullanmaktır .

Swift 5 :

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

collectionView.reloadData()

CATransaction.commit()

0

Bunu yapın:

//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
    }

}

O zaman VC'nizde böyle arayın

let collection = MyCollectionView()

self.collection.reloadData(completion: {

})

Alt sınıfı kullandığınızdan emin olun !!


0

Bu benim için çalışıyor:


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

}


0

Koleksiyonu yeniden yükleyin, toplu güncellemeleri görüntüleyin ve ardından tamamlama bloğunun bitip bitmediğini boolean "bitiş" yardımıyla kontrol edin.

self.collectionView.performBatchUpdates({
        self.collectionView.reloadData()
    }) { (finish) in
        if finish{
            // Do your stuff here!
        }
    }

0

Benim için işe yarayan tek yaklaşım aşağıdadır.

extension UICollectionView {
    func reloadData(_ completion: (() -> Void)? = nil) {
        reloadData()
        guard let completion = completion else { return }
        layoutIfNeeded()
        completion()
    }
}

-1

Swift 3.0 ile problemi böyle çözdüm:

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

    if !self.collectionView.visibleCells.isEmpty {
        // stuff
    }
}

Bu işe yaramayacak. VisibleCells, ilk hücre yüklenir yüklenmez içeriğe sahip olacak ... sorun şu ki, son hücrenin ne zaman yüklendiğini bilmek istiyoruz.
Travis M.

-9

Bunu dene:

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

2
Bu doğru değil, cellFor yalnızca bu hücre görünür olacaksa çağrılacak, bu durumda görünen son öğe toplam öğe sayısına eşit olmayacak ...
Jirune

-10

Böyle yapabilirsin ...

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

    }

Bu kod soruyu yanıtlasa da, soruyu neden ve / veya nasıl yanıtladığına ilişkin ek bağlam sağlamak , uzun vadeli değerini önemli ölçüde artıracaktır. Lütfen bir açıklama eklemek için cevabınızı düzenleyin .
Toby Speight
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.