मुझे कैसे पता चलेगा कि UICollectionView को पूरी तरह से लोड किया गया है?


98

जब भी UICollectionView को पूरी तरह से लोड किया गया है, तो मुझे कुछ ऑपरेशन करना होगा, अर्थात उस समय सभी UICollectionView के डेटा स्रोत / लेआउट विधियों को बुलाया जाना चाहिए। मुझे इस बात की जानकारी कैसे होगी?? UICollectionView भरी हुई स्थिति जानने के लिए क्या कोई प्रतिनिधि विधि है?

जवाबों:


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
जब मैं वापस नेविगेट करता हूं तो यह मेरे लिए क्रैश हो जाता है, अन्यथा अच्छा काम करता है।
एरिकोसग

4
@ericosg जोड़ने [self.collectionView removeObserver:self forKeyPath:@"contentSize" context:NULL];के लिए -viewWillDisappear:animated:के रूप में अच्छी तरह से।
pxpgraphics

प्रतिभाशाली! आपका बहुत बहुत धन्यवाद!
अब्दो

41
स्क्रॉल करते समय सामग्री का आकार बदल जाता है इसलिए यह विश्वसनीय नहीं है।
रयान हेटनर

मैंने एक संग्रह दृश्य के लिए कोशिश की है जो एक async बाकी विधि का उपयोग करके डेटा प्राप्त करता है। इसका मतलब यह है कि जब यह विधि आग लग जाती है, तो विज़िबल सेल हमेशा खाली रहता है, जिसका अर्थ है कि मैं शुरू में संग्रह में एक आइटम पर स्क्रॉल करता हूं।
Chucky

155

यह मेरे लिए काम किया:

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

स्विफ्ट 4 सिंटैक्स:

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

1
IOS 9 पर मेरे लिए काम किया
Magoo

5
@ G.Abhisek Errrr, निश्चित रूप से .... आपको समझाने की क्या आवश्यकता है? पहली पंक्ति फिर से reloadDataताज़ा हो जाती है, collectionViewइसलिए यह फिर से डेटा स्रोत विधियों पर आ जाती है ... performBatchUpdatesएक पूर्ण ब्लॉक संलग्न है अगर दोनों को मुख्य धागे पर किया जाता है, तो आपको पता है कि किसी भी कोड को डाल दिया /// collection-view finished reloadजाएगा जो एक ताजा और निर्धारित के साथ निष्पादित होगाcollectionView
मागू

8
मेरे लिए तब काम नहीं किया जब संग्रह
दृश्य में

2
यह विधि बिना किसी सिरदर्द के सही समाधान है।
अरजवल्द

1
@ingaham यह डायनामिक आकार की कोशिकाओं के साथ मेरे लिए पूरी तरह से काम करता है, स्विफ्ट 4.2 IOS 12
रोमुलो बीएम

27

यह वास्तव में बहुत सरल है।

जब आप उदाहरण के लिए UICollectionView की पुनः लोड विधि को कॉल करते हैं या यह लेआउट की अमान्य विधि है, तो आप निम्न कार्य करते हैं:

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

यह क्यों काम करता है:

मुख्य धागा (जो कि हमें सभी यूआई अपडेट करना चाहिए) में मुख्य कतार है, जो प्रकृति में धारावाहिक है, अर्थात यह एफआईएफओ फैशन में काम करता है। इसलिए उपरोक्त उदाहरण में, पहले ब्लॉक को कॉल किया जाता है, जिसमें हमारी reloadDataविधि को आमंत्रित किया जाता है, इसके बाद दूसरे ब्लॉक में कुछ भी होता है।

अब मुख्य धागा भी अवरुद्ध हो रहा है। इसलिए यदि आपको reloadDataनिष्पादन के लिए 3s लगते हैं , तो दूसरे ब्लॉक के प्रसंस्करण को उन 3s द्वारा स्थगित कर दिया जाएगा।


2
मैंने वास्तव में सिर्फ इसका परीक्षण किया है, ब्लॉक को मेरे अन्य सभी प्रतिनिधि तरीकों से पहले बुलाया जा रहा है। तो यह काम नहीं करता है?
taylorcressy 23

@taylorcressy हे, मैंने कोड को अपडेट किया है, कुछ ऐसा जो मैं अब 2 उत्पादन ऐप में उपयोग कर रहा हूं। स्पष्टीकरण भी शामिल किया।
dezinezync

मेरे लिए काम नहीं करता है। जब मैं reloadData कहता हूं, तो संग्रह दृश्य दूसरे ब्लॉक के निष्पादन के बाद कोशिकाओं का अनुरोध करता है। इसलिए मुझे @taylorcressy जैसी ही समस्या हो रही है।
राफेल

1
क्या आप पहले के भीतर दूसरा ब्लॉक कॉल डालने की कोशिश कर सकते हैं? असत्यापित, लेकिन काम कर सकता है।
dezinezync

6
reloadDataअब मुख्य थ्रेड (भले ही मुख्य थ्रेड से कहा जाता है) पर कोशिकाओं के लोडिंग को लागू करता है। इसलिए सेल वास्तव में रिटर्न के cellForRowAtIndexPath:बाद लोड नहीं किया जाता है ( नहीं कहा जाता है) reloadData। रैपिंग कोड जिसे आप अपनी कोशिकाओं में लोड होने के बाद निष्पादित करना चाहते हैं dispatch_async(dispatch_get_main...और कॉल कर रहे हैं जो आपके कॉल के बाद reloadDataवांछित परिणाम प्राप्त कर सकते हैं।
Tylerc230

12

बस एक महान @dezinezync उत्तर में जोड़ने के लिए:

स्विफ्ट 3+

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

@ इसको हमें देखने के लिए जोड़ना होगा !!
साराथ SAS

1
जरूरी नहीं कि लेआउट के आखिरकार लोड होने पर आप इसे जहां से भी कुछ करना चाहते हैं, वहां से बुला सकते हैं।
निकन्स

मुझे स्क्रॉल फ़्लिकर मिल रहे थे, जो आइटम डालने के बाद मेरे कलेक्शन को स्क्रॉल करने की कोशिश कर रहे थे, यह तय हो गया, धन्यवाद।
Skoua

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

4

पुनः लोडडैट () कॉल के ठीक बाद लेआउटआईएफएनएडेड () के माध्यम से एक तुल्यकालिक लेआउट पास की कोशिश करें। IOS 12 पर UICollectionView और UITableView दोनों के लिए काम करता है।

collectionView.reloadData()
collectionView.layoutIfNeeded() 

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

धन्यवाद दोस्त! मैं काफी लंबे समय से उस समस्या का हल ढूंढ रहा हूं।
Trzy Gracje

3

के रूप में dezinezync ने उत्तर दिया, क्या आप की जरूरत के बाद कोड का एक खंड मुख्य कतार में प्रेषण करने के लिए है reloadDataएक से UITableViewया UICollectionView, और उसके बाद इस ब्लॉक कोशिकाओं के बाद निष्पादित किया जाएगा dequeuing

उपयोग करते समय इसे और अधिक सीधा बनाने के लिए, मैं इस तरह से एक एक्सटेंशन का उपयोग करूंगा:

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

इसे एक UITableViewअच्छी तरह से भी लागू किया जा सकता है


3

RxSwift / RxCocoa का उपयोग करते हुए एक अलग तरीका:

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

1
मुझे यह पसंद आ रहा है। performBatchUpdatesमेरे मामले में काम नहीं कर रहा था, यह एक करता है। चीयर्स!
ज़ोल्टन 14

@ MichałZiobro, क्या आप कहीं अपना कोड अपडेट कर सकते हैं? जब सामग्री को परिवर्तित किया जाता है तो विधि को केवल तभी बुलाया जाना चाहिए? इसलिए, जब तक आप इसे हर स्क्रॉल पर नहीं बदलते हैं, तब तक यह काम करना चाहिए!
चिनह गुयेन

1

यह मेरे लिए काम करता है:

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

2
बकवास ... यह किसी भी तरह से समाप्त नहीं हुआ है।
मैगू

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
}

प्रारंभिक अवस्था में दृश्य कोशिकाओं की संख्या के रूप में कार्रवाई अभी भी कई बार की जाएगी। लेकिन उन सभी कॉल पर, आपके पास दृश्यमान कोशिकाओं (उन सभी) की संख्या समान होगी। और उपयोगकर्ता द्वारा संग्रह दृश्य के साथ बातचीत शुरू करने के बाद बूलियन ध्वज फिर से चलने से रोक देगा।


1

मैं सिर्फ संग्रह दृश्य पुनः लोड होने के बाद कुछ भी करने के लिए निम्नलिखित किया था। आप एपीआई प्रतिक्रिया में भी इस कोड का उपयोग कर सकते हैं।

self.collectionView.reloadData()

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

1

सबसे अच्छा समाधान जो मैंने अभी तक पाया है, उसे CATransactionपूरा करने के लिए उपयोग करना है।

स्विफ्ट 5 :

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

collectionView.reloadData()

CATransaction.commit()

0

यह करें:

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

}

फिर अपने वीसी के अंदर इस तरह से कॉल करें

let collection = MyCollectionView()

self.collection.reloadData(completion: {

})

सुनिश्चित करें कि आप उपवर्ग का उपयोग कर रहे हैं !!


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

}


0

बस बैच अपडेट के अंदर संग्रह दृश्य पुनः लोड करें और फिर पूर्ण ब्लॉक में जांचें कि क्या यह बूलियन "फिनिश" की मदद से समाप्त हो गया है या नहीं।

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

0

नीचे केवल वही तरीका है जो मेरे लिए काम करता है।

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

-1

इस तरह मैंने स्विफ्ट 3.0 के साथ समस्या हल की है:

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

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

यह काम नहीं करेगा। पहली सेल लोड होते ही VisibleCells में सामग्री होगी ... समस्या यह है कि हम जानना चाहते हैं कि आखिरी सेल कब लोड हुई है।
ट्रैविस एम।

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

2
यह सही नहीं है, सेलफोर को केवल तभी कॉल किया जाएगा जब वह सेल दिखाई देने वाली होगी, उस स्थिति में अंतिम दृश्यमान आइटम कुल आइटम की संख्या के बराबर नहीं होगा ...
Jirune

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

    }

यद्यपि यह कोड प्रश्न का उत्तर दे सकता है, क्यों और / या कैसे का उत्तर देता है के संबंध में अतिरिक्त संदर्भ प्रदान करने से इसके दीर्घकालिक मूल्य में काफी सुधार होगा। कृपया कुछ स्पष्टीकरण जोड़ने के लिए अपने उत्तर को संपादित करें।
स्पाइट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.