जब UITableView ने ReloadData पूरा किया है तो कैसे बताएं?


183

मैं प्रदर्शन करने के बाद UITableView की तह तक जाने की कोशिश कर रहा हूं [self.tableView reloadData]

मैं मूल रूप से था

 [self.tableView reloadData]
 NSIndexPath* indexPath = [NSIndexPath indexPathForRow: ([self.tableView numberOfRowsInSection:([self.tableView numberOfSections]-1)]-1) inSection: ([self.tableView numberOfSections]-1)];

[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];

लेकिन फिर मैंने पढ़ा कि पुनः लोड करना अतुल्यकालिक है, इसलिए स्क्रॉलिंग तब से नहीं होती है self.tableView, [self.tableView numberOfSections]और[self.tableView numberOfRowsinSection सभी 0 हैं।

धन्यवाद!

क्या अजीब है कि मैं उपयोग कर रहा हूँ:

[self.tableView reloadData];
NSLog(@"Number of Sections %d", [self.tableView numberOfSections]);
NSLog(@"Number of Rows %d", [self.tableView numberOfRowsInSection:([self.tableView numberOfSections]-1)]-1);

कंसोल में यह धारा = 1, पंक्ति = -1 लौटाता है;

जब मैं ठीक वही NSLogs करता हूं जिसमें cellForRowAtIndexPathमुझे धारा = 1 और पंक्ति = 8 मिलती है; (8 सही है)


इस प्रश्न का संभावित डुप्लिकेट: stackoverflow.com/questions/4163579/…
pmk

2
सबसे अच्छा समाधान मैंने देखा है। stackoverflow.com/questions/1483581/…
खालिद अन्नजर

निम्नलिखित के लिए मेरा उत्तर आपकी मदद कर सकता है, stackoverflow.com/questions/4163579/…
सुहास एिथल

यहाँ मेरे उत्तर का प्रयास करें - stackoverflow.com/questions/4163579/…
सुहास एिथल

जवाबों:


288

पुनः लोड अगले लेआउट पास के दौरान होता है, जो सामान्य रूप से तब होता है जब आप रन लूप पर नियंत्रण लौटाते हैं (बाद में, कहते हैं, आपकी बटन कार्रवाई या अन्य रिटर्न)।

टेबल व्यू रीलोड के बाद कुछ चलाने का एक तरीका है कि टेबल व्यू को तुरंत लेआउट करने के लिए मजबूर किया जाए:

[self.tableView reloadData];
[self.tableView layoutIfNeeded];
 NSIndexPath* indexPath = [NSIndexPath indexPathForRow: ([self.tableView numberOfRowsInSection:([self.tableView numberOfSections]-1)]-1) inSection: ([self.tableView numberOfSections]-1)];
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];

दूसरा तरीका बाद में उपयोग करने के लिए अपने आफ्टर-लेआउट कोड को शेड्यूल करना है dispatch_async:

[self.tableView reloadData];

dispatch_async(dispatch_get_main_queue(), ^{
     NSIndexPath* indexPath = [NSIndexPath indexPathForRow: ([self.tableView numberOfRowsInSection:([self.tableView numberOfSections]-1)]-1) inSection:([self.tableView numberOfSections]-1)];

    [self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
});

अपडेट करें

आगे की जाँच करने पर, मुझे पता चलता है कि टेबल व्यू भेजता है tableView:numberOfSections:और tableView:numberOfRowsInSection:उसके डेटा स्रोत से लौटने से पहले reloadData। यदि प्रतिनिधि को लागू करता है tableView:heightForRowAtIndexPath:, तो तालिका दृश्य भी (प्रत्येक पंक्ति के लिए) से लौटने से पहले भेजता है reloadData

हालाँकि, तालिका दृश्य लेआउट चरण तक tableView:cellForRowAtIndexPath:या tableView:headerViewForSectionजब तक नहीं भेजती है , जो डिफ़ॉल्ट रूप से तब होता है जब आप रन लूप पर नियंत्रण लौटाते हैं।

मुझे यह भी पता चलता है कि एक छोटे से परीक्षण कार्यक्रम में, आपके प्रश्न का कोड ठीक से टेबल व्यू के नीचे स्क्रॉल करता है, मेरे बिना कुछ भी विशेष करने (जैसे भेजने layoutIfNeededया उपयोग करने dispatch_async) के बिना।


3
@rob, आपका टेबल डेटा स्रोत कितना बड़ा है, इसके आधार पर, आप एक ही रन लूप में टेबलव्यू के निचले भाग में जा सकते हैं। यदि आप अपने परीक्षण कोड को एक विशाल तालिका के साथ आज़माते हैं, तो स्क्रॉल को विलंबित करने के लिए GCD का उपयोग करने की आपकी चाल तब तक चलेगी जब तक कि अगला रन लूप काम नहीं करेगा, जबकि तुरंत स्क्रॉल करना विफल हो जाएगा। लेकिन वैसे भी, इस चाल के लिए धन्यवाद !!
मिस्टर टी

7
विधि 2 ने किसी अज्ञात कारण से मेरे लिए काम नहीं किया, बल्कि इसके बजाय पहली विधि को चुना।
राज पवन गुमडल

4
dispatch_async(dispatch_get_main_queue())विधि काम की गारंटी नहीं है। मैं इसके साथ गैर-निर्धारक व्यवहार देख रहा हूं, जिसमें कभी-कभी सिस्टम ने लेआउटसुब्यू और सेल रेंडरिंग को पूरा ब्लॉक से पहले और कभी-कभी पूरा किया है। मैं एक जवाब पोस्ट करूंगा जो मेरे लिए नीचे काम करता है।
टायलर शीफ़े

3
dispatch_async(dispatch_get_main_queue())हमेशा काम नहीं करने से सहमत । यहां यादृच्छिक परिणाम देख रहे हैं।
वोजतो

1
मुख्य धागा एक चलाता है NSRunLoop। एक रन लूप में अलग-अलग चरण होते हैं, और आप एक विशिष्ट चरण (उपयोग करके CFRunLoopObserver) के लिए कॉलबैक शेड्यूल कर सकते हैं । आपके इवेंट हैंडलर के वापस आने के बाद UIKit शेड्यूल लेआउट बाद के चरण में होगा।
18

106

स्विफ्ट:

extension UITableView {
    func reloadData(completion:@escaping ()->()) {
        UIView.animateWithDuration(0, animations: { self.reloadData() })
            { _ in completion() }
    }
}

...somewhere later...

tableView.reloadData {
    println("done")
}

उद्देश्य सी:

[UIView animateWithDuration:0 animations:^{
    [myTableView reloadData];
} completion:^(BOOL finished) {
    //Do something after that...
}];

16
यह "निकट भविष्य" में मुख्य धागे पर कुछ भेजने के बराबर है। यह संभव है कि आप सिर्फ टेबल व्यू देख रहे हों, मुख्य थ्रेड को पूरा करने से पहले ऑब्जेक्ट को रेंडर करें। पहली बार में इस तरह की हैकिंग करने की सलाह नहीं दी गई है, लेकिन किसी भी स्थिति में, आपको डिस्पैच_आफ्टर का उपयोग करना चाहिए यदि आप यह प्रयास करने जा रहे हैं।
एसईओ

1
रोब का समाधान अच्छा है, लेकिन अगर तालिका में कोई पंक्तियाँ नहीं हैं तो काम नहीं करता है। एविएल के समाधान में काम करने का लाभ तब भी है जब तालिका में कोई रेखाएं नहीं होती हैं, लेकिन केवल अनुभाग होते हैं।
Chrstph SLN

@Christophe अब तक, मैं अपने मॉक व्यू कंट्रोलर में tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Intविधि को ओवरराइड करके और अपने ओवरराइड में जो कुछ भी मुझे सूचित करना चाहता था कि पुनः लोड हो चुका था, में रोब की अपडेट को बिना किसी पंक्तियों के उपयोग करने में सक्षम था।
Gobe

49

Xcode 8.2.1, iOS 10 और स्विफ्ट 3 के रूप में,

आप tableView.reloadData()कैटरेन्सेशन ब्लॉक का उपयोग करके आसानी से अंत का निर्धारण कर सकते हैं :

CATransaction.begin()
CATransaction.setCompletionBlock({
    print("reload completed")
    //Your completion code here
})
print("reloading")
tableView.reloadData()
CATransaction.commit()

ऊपर भी UICollectionView के रीलोडडाटा () और UIPickerView के reloadAllCompords () के अंत का निर्धारण करने के लिए काम करता है।


Like मैं यह भी काम करता हूं यदि आप कस्टम रीलोडिंग का प्रदर्शन कर रहे हैं, जैसे कि टेबल व्यू में पंक्तियों को मैन्युअल रूप से सम्मिलित करना, हटाना या स्थानांतरित करना, भीतर beginUpdatesऔर endUpdatesकॉल।
दाररस्की

मेरा मानना ​​है कि यह वास्तव में आधुनिक समाधान है। वास्तव में आईओएस में यह सामान्य पैटर्न है, उदाहरण के लिए ... stackoverflow.com/a/47536770/294884
फेटी

मैंने यह कोशिश की। मुझे बहुत अजीब व्यवहार मिला। मेरा टेबलव्यू दो शीर्ष लेख दिखाता है। setCompletionBlockमेरे numberOfSectionsशो 2 के अंदर ... अब तक बहुत अच्छा। फिर भी अगर setCompletionBlockमैं अंदर करता हूँ तो tableView.headerView(forSection: 1)यह वापस आ जाता है nil!!! इसलिए मुझे लगता है कि यह ब्लॉक या तो रीलोड होने से पहले हो रहा है या कुछ पहले कैप्चर कर रहा है या मैं कुछ गलत कर रहा हूं। FYI करें मैंने टायलर के जवाब की कोशिश की और वह काम कर गया! @ फेटी
हनी

32

dispatch_async(dispatch_get_main_queue())ऊपर की विधि काम करने की गारंटी नहीं है । मैं इसके साथ गैर-नियतात्मक व्यवहार देख रहा हूं, जिसमें कभी-कभी सिस्टम ने लेआउटब्लॉब्स और सेल रेंडरिंग को पूर्ण ब्लॉक से पहले और कभी-कभी पूरा किया है।

यहां एक समाधान है जो आईओएस 10 पर मेरे लिए 100% काम करता है। इसके लिए कस्टम सबक्लास के रूप में यूआईटेबल व्यू या यूआईसीओलेक्लेओन व्यू को इंस्टेंट करने की क्षमता की आवश्यकता है। यहाँ UICollectionView समाधान है, लेकिन यह UITableView के लिए बिल्कुल वैसा ही है:

CustomCollectionView.h:

#import <UIKit/UIKit.h>

@interface CustomCollectionView: UICollectionView

- (void)reloadDataWithCompletion:(void (^)(void))completionBlock;

@end

CustomCollectionView.m:

#import "CustomCollectionView.h"

@interface CustomCollectionView ()

@property (nonatomic, copy) void (^reloadDataCompletionBlock)(void);

@end

@implementation CustomCollectionView

- (void)reloadDataWithCompletion:(void (^)(void))completionBlock
{
    self.reloadDataCompletionBlock = completionBlock;
    [self reloadData];
}

- (void)layoutSubviews
{
    [super layoutSubviews];

    if (self.reloadDataCompletionBlock) {
        self.reloadDataCompletionBlock();
        self.reloadDataCompletionBlock = nil;
    }
}

@end

उदाहरण उपयोग:

[self.collectionView reloadDataWithCompletion:^{
    // reloadData is guaranteed to have completed
}];

इस उत्तर के स्विफ्ट संस्करण के लिए यहां देखें


यह एकमात्र सही तरीका है। इसे अपनी परियोजना में जोड़ा क्योंकि मुझे एनीमेशन उद्देश्यों के लिए कुछ कोशिकाओं के अंतिम फ्रेम की आवश्यकता थी। मैंने स्विफ्ट के लिए भी जोड़ा और संपादित किया। आशा है कि आप बुरा नहीं मानेंगे
जॉन वोगेल

2
आपके द्वारा ब्लॉक को कॉल करने के बाद layoutSubviewsइसे nilबाद की कॉल के रूप में सेट किया जाना चाहिए layoutSubviews, जरूरी नहीं कि reloadDataबुलाए जाने के कारण , ब्लॉक में निष्पादित किया जाएगा क्योंकि एक मजबूत संदर्भ आयोजित किया जा रहा है, जो वांछित व्यवहार नहीं है।
मार्क बोर्के

मैं UITableView के लिए इसका उपयोग क्यों नहीं कर सकता? यह कोई दृश्यमान इंटरफ़ेस नहीं दिखा रहा है। मैंने हेडर फ़ाइल भी आयात की लेकिन फिर भी वही
जुल्फिकार

2
इस उत्तर के लिए एक परिशिष्ट यह है कि मौजूदा कॉलबैक को रोकना संभव है यदि एक ही है, तो इसका अर्थ है कि कई कॉल करने वालों की दौड़-हालत होगी। समाधान reloadDataCompletionBlockब्लॉक की एक सरणी बनाने और निष्पादन पर उन पर पुनरावृति करने और उसके बाद सरणी खाली करने के लिए है।
टायलर शीफ़र

1) क्या यह रोब के पहले उत्तर के बराबर नहीं है यानी कि लेआउट का उपयोग करने के लिए। 2) आपने iOS 10 का उल्लेख क्यों किया, क्या यह iOS 9 पर काम नहीं करता है ?!
हनी

30

मेरे पास टायलर शफ़र के समान ही मुद्दे थे।

मैंने स्विफ्ट में उसका समाधान लागू किया और इसने मेरी समस्याओं को हल किया।

स्विफ्ट 3.0:

final class UITableViewWithReloadCompletion: UITableView {
  private var reloadDataCompletionBlock: (() -> Void)?

  override func layoutSubviews() {
    super.layoutSubviews()

    reloadDataCompletionBlock?()
    reloadDataCompletionBlock = nil
  }


  func reloadDataWithCompletion(completion: @escaping () -> Void) {
    reloadDataCompletionBlock = completion
    self.reloadData()
  }
}

स्विफ्ट 2:

class UITableViewWithReloadCompletion: UITableView {

  var reloadDataCompletionBlock: (() -> Void)?

  override func layoutSubviews() {
    super.layoutSubviews()

    self.reloadDataCompletionBlock?()
    self.reloadDataCompletionBlock = nil
  }

  func reloadDataWithCompletion(completion:() -> Void) {
      reloadDataCompletionBlock = completion
      self.reloadData()
  }
}

उदाहरण उपयोग:

tableView.reloadDataWithCompletion() {
 // reloadData is guaranteed to have completed
}

1
अच्छा! छोटे नाइट-पिक, आप if letयह कहकर हटा सकते हैं कि reloadDataCompletionBlock?()जो iff नहीं nil को कॉल करेगा
टायलर शीफ़र

मेरी किस्मत में ios9 पर इस एक के साथ कोई भाग्य नहीं है
Matjan

self.reloadDataCompletionBlock? { completion() }किया जाना चाहिए थाself.reloadDataCompletionBlock?()
EMEM

मैं टेबल दृश्य ऊंचाई के आकार को कैसे संभाल सकता हूं? मैं पहले tableView.beginUpdates () tableView.layoutIfNeeded () tableView.endUpdates ()
पार्थ तामने

10

और ए UICollectionView kolaworld के उत्तर पर आधारित संस्करण:

https://stackoverflow.com/a/43162226/1452758

परीक्षण की आवश्यकता है। आईओएस 9.2, एक्सकोड 9.2 बीटा 2 पर अब तक एक कलेक्शन व्यू को इंडेक्स पर स्क्रॉल करने के साथ बंद करने के रूप में काम करता है।

extension UICollectionView
{
    /// Calls reloadsData() on self, and ensures that the given closure is
    /// called after reloadData() has been completed.
    ///
    /// Discussion: reloadData() appears to be asynchronous. i.e. the
    /// reloading actually happens during the next layout pass. So, doing
    /// things like scrolling the collectionView immediately after a
    /// call to reloadData() can cause trouble.
    ///
    /// This method uses CATransaction to schedule the closure.

    func reloadDataThenPerform(_ closure: @escaping (() -> Void))
    {       
        CATransaction.begin()
            CATransaction.setCompletionBlock(closure)
            self.reloadData()
        CATransaction.commit()
    }
}

उपयोग:

myCollectionView.reloadDataThenPerform {
    myCollectionView.scrollToItem(at: indexPath,
            at: .centeredVertically,
            animated: true)
}

6

ऐसा लगता है कि लोग अभी भी इस सवाल और जवाब को पढ़ रहे हैं। उस के B / c, मैं सिंक्रोनस शब्द को हटाने के लिए अपना उत्तर संपादित कर रहा हूं जो वास्तव में इसके लिए अप्रासंगिक है।

When [tableView reloadData]रिटर्न, टेबल व्यू के पीछे की आंतरिक डेटा संरचनाओं को अपडेट किया गया है। इसलिए, जब विधि पूरी हो जाती है तो आप सुरक्षित रूप से नीचे स्क्रॉल कर सकते हैं। मैंने इसे अपने ऐप में सत्यापित किया। @ रॉब-मेऑफ़ द्वारा व्यापक रूप से स्वीकृत उत्तर, जबकि शब्दावली में भी भ्रमित, अपने अंतिम अपडेट में भी यही स्वीकार करता है।

यदि आपका tableViewनीचे तक स्क्रॉल नहीं हो रहा है, तो आपके पास अन्य कोड में कोई समस्या हो सकती है जिसे आपने पोस्ट नहीं किया है। शायद आप स्क्रॉल पूरा होने के बाद डेटा बदल रहे हैं और आप पुनः लोड नहीं कर रहे हैं और / या फिर नीचे तक स्क्रॉल कर रहे हैं?

तालिका डेटा सही होने के बाद सत्यापित करने के लिए कुछ लॉगिंग निम्नानुसार जोड़ें reloadData। मेरे पास एक नमूना ऐप में निम्नलिखित कोड है और यह पूरी तरह से काम करता है।

// change the data source

NSLog(@"Before reload / sections = %d, last row = %d",
      [self.tableView numberOfSections],
      [self.tableView numberOfRowsInSection:[self.tableView numberOfSections]-1]);

[self.tableView reloadData];

NSLog(@"After reload / sections = %d, last row = %d",
      [self.tableView numberOfSections],
      [self.tableView numberOfRowsInSection:[self.tableView numberOfSections]-1]);

[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:[self.tableView numberOfRowsInSection:[self.tableView numberOfSections]-1]-1
                                                          inSection:[self.tableView numberOfSections] - 1]
                      atScrollPosition:UITableViewScrollPositionBottom
                              animated:YES];

मैंने अपने सवालों को अपडेट किया। क्या आप जानते हैं कि मेरे NSLogs इस तरह क्यों आउटपुट करेंगे?
एलन

8
reloadDataसमकालिक नहीं है। यह हुआ करता था - इस उत्तर को देखें: stackoverflow.com/a/16071589/193896
bendytree

1
यह समकालिक है। यह एक नमूना ऐप के साथ परीक्षण और देखना बहुत आसान है। आप इस सवाल में @ रोब के जवाब से जुड़े हैं। यदि आप उसके अपडेट को सबसे नीचे पढ़ते हैं, तो उसने इसे सत्यापित भी कर दिया है। शायद आप दृश्य लेआउट परिवर्तनों के बारे में बात कर रहे हैं। यह सच है कि तालिका दृश्य को समकालिक रूप से अद्यतन नहीं किया जाता है, लेकिन डेटा है। इसीलिए reloadDataरिटर्न के तुरंत बाद ओपी की जरूरतें सही हैं ।
XJones

1
आप इस बारे में भ्रमित हो सकते हैं कि इसमें क्या होने की उम्मीद है reloadData। यदि प्रदर्शित नहीं की जाती है तो लाइन b / c के viewWillAppearलिए स्वीकार में मेरे परीक्षण मामले का उपयोग करें । आप देखेंगे कि उदाहरण में कैश्ड डेटा को अपडेट किया और वह सिंक्रोनस है। यदि आप अन्य प्रतिनिधि विधियों का जिक्र कर रहे हैं, जब कहा जा रहा है कि लेआउट प्रदर्शित नहीं किया जा रहा है, तो उन्हें प्रदर्शित नहीं किया जाएगा। अगर मैं आपके परिदृश्य को गलत समझ रहा हूं तो कृपया समझाएं। scrollToRowAtIndexPath:tableViewreloadDatatableViewreloadDatatableViewtableViewtableView
XJones

3
क्या मज़ा समय है यह 2014 है, और इस बात पर बहस चल रही है कि कुछ विधि तुल्यकालिक और अतुल्यकालिक है या नहीं। अनुमान लगता है। सभी कार्यान्वयन विवरण उस विधि के नाम के पीछे पूरी तरह से अपारदर्शी है। क्या प्रोग्रामिंग शानदार नहीं है?
फतुहोकू

5

मैं इस ट्रिक का उपयोग करता हूँ, मुझे यकीन है कि मैंने पहले ही इस प्रश्न के डुप्लिकेट पर पोस्ट कर दिया था:

-(void)tableViewDidLoadRows:(UITableView *)tableView{
    // do something after loading, e.g. select a cell.
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    // trick to detect when table view has finished loading.
    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(tableViewDidLoadRows:) object:tableView];
    [self performSelector:@selector(tableViewDidLoadRows:) withObject:tableView afterDelay:0];

    // specific to your controller
    return self.objects.count;
}

@ फैटी यह स्पष्ट नहीं है यदि आप इसका मतलब सकारात्मक टिप्पणी या नकारात्मक टिप्पणी के रूप में करते हैं। लेकिन मैंने देखा कि आपने एक और जवाब दिया है "यह सबसे अच्छा समाधान है!" , इसलिए मुझे लगता है कि अपेक्षाकृत अधिक बोलते हुए, आप इस समाधान को सर्वश्रेष्ठ नहीं मानते हैं।
कूर

1
एक नकली एनीमेशन के एक पक्ष पर निर्भर? कोई रास्ता नहीं है कि एक अच्छा विचार है। चयनकर्ता या GCD जानें और इसे ठीक से करें। Btw अब एक टेबल लोडेड विधि है जिसका आप उपयोग कर सकते हैं यदि आप एक निजी प्रोटोकॉल का उपयोग करने में कोई आपत्ति नहीं करते हैं जो कि ठीक है क्योंकि यह फ्रेमवर्क है जो आपके कोड को कॉल करने के बजाय अन्य तरीके से करता है।
मलय

3

वास्तव में इसने मेरी समस्या हल कर दी:

-(void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {

NSSet *visibleSections = [NSSet setWithArray:[[tableView indexPathsForVisibleRows] valueForKey:@"section"]];
if (visibleSections) {
    // hide the activityIndicator/Loader
}}

2

इस तरह से प्रयास करें यह काम करेगा

[tblViewTerms performSelectorOnMainThread:@selector(dataLoadDoneWithLastTermIndex:) withObject:lastTermIndex waitUntilDone:YES];waitUntilDone:YES];

@interface UITableView (TableViewCompletion)

-(void)dataLoadDoneWithLastTermIndex:(NSNumber*)lastTermIndex;

@end

@implementation UITableView(TableViewCompletion)

-(void)dataLoadDoneWithLastTermIndex:(NSNumber*)lastTermIndex
{
    NSLog(@"dataLoadDone");


NSIndexPath* indexPath = [NSIndexPath indexPathForRow: [lastTermIndex integerValue] inSection: 0];

[self selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionNone];

}
@end

मैं पूरी तरह से लोड होने पर निष्पादित करूंगा

अन्य समाधान आप UITableView को उप-वर्ग कर सकते हैं


1

मैंने शॉन के समाधान की भिन्नता का उपयोग करते हुए समाप्त किया:

एक प्रतिनिधि के साथ एक कस्टम UITableView वर्ग बनाएं:

protocol CustomTableViewDelegate {
    func CustomTableViewDidLayoutSubviews()
}

class CustomTableView: UITableView {

    var customDelegate: CustomTableViewDelegate?

    override func layoutSubviews() {
        super.layoutSubviews()
        self.customDelegate?.CustomTableViewDidLayoutSubviews()
    }
}

फिर मेरे कोड में, मैं उपयोग करता हूं

class SomeClass: UIViewController, CustomTableViewDelegate {

    @IBOutlet weak var myTableView: CustomTableView!

    override func viewDidLoad() {
        super.viewDidLoad()

        self.myTableView.customDelegate = self
    }

    func CustomTableViewDidLayoutSubviews() {
        print("didlayoutsubviews")
        // DO other cool things here!!
    }
}

यह भी सुनिश्चित करें कि आपने इंटरफ़ेस बिल्डर में CustomTableView को अपना टेबल दृश्य सेट किया है:

यहां छवि विवरण दर्ज करें


यह काम करता है, लेकिन समस्या यह है कि यह विधि हर बार हिट हो जाती है जब एक एकल सेल को लोड किया जाता है, न कि पूरे टेबल व्यू रिलोड, इसलिए स्पष्ट रूप से पूछे गए प्रश्न के संबंध में यह उत्तर नहीं है।
यश बेदी

सच है, इसे एक से अधिक बार कॉल किया जाता है, लेकिन हर सेल पर नहीं। इसलिए आप पहले प्रतिनिधि को सुन सकते हैं और बाकी को अनदेखा कर सकते हैं जब तक कि आप फिर से लोड नहीं करते हैं।
सैम

1

स्विफ्ट 3.0 + में हम नीचे के UITableViewसाथ एक एक्सटेंशन बना सकते हैं escaped Closure:

extension UITableView {
    func reloadData(completion: @escaping () -> ()) {
        UIView.animate(withDuration: 0, animations: { self.reloadData()})
        {_ in completion() }
    }
}

और इसे नीचे की तरह उपयोग करें जहाँ आप कभी भी चाहते हैं:

Your_Table_View.reloadData {
   print("reload done")
 }

आशा है कि यह किसी की मदद करेगा। चियर्स!


शानदार विचार..एक ही बात, भ्रम से बचने के लिए है, मैंने पुनः लोडडाटा () के बजाय फ़ंक्शन नाम टी पुनः लोड किया है। धन्यवाद
विजय कुमार एबी

1

विवरण

  • Xcode संस्करण 10.2.1 (10E1001), स्विफ्ट 5

उपाय

import UIKit

// MARK: - UITableView reloading functions

protocol ReloadCompletable: class { func reloadData() }

extension ReloadCompletable {
    func run(transaction closure: (() -> Void)?, completion: (() -> Void)?) {
        guard let closure = closure else { return }
        CATransaction.begin()
        CATransaction.setCompletionBlock(completion)
        closure()
        CATransaction.commit()
    }

    func run(transaction closure: (() -> Void)?, completion: ((Self) -> Void)?) {
        run(transaction: closure) { [weak self] in
            guard let self = self else { return }
            completion?(self)
        }
    }

    func reloadData(completion closure: ((Self) -> Void)?) {
        run(transaction: { [weak self] in self?.reloadData() }, completion: closure)
    }
}

// MARK: - UITableView reloading functions

extension ReloadCompletable where Self: UITableView {
    func reloadRows(at indexPaths: [IndexPath], with animation: UITableView.RowAnimation, completion closure: ((Self) -> Void)?) {
        run(transaction: { [weak self] in self?.reloadRows(at: indexPaths, with: animation) }, completion: closure)
    }

    func reloadSections(_ sections: IndexSet, with animation: UITableView.RowAnimation, completion closure: ((Self) -> Void)?) {
        run(transaction: { [weak self] in self?.reloadSections(sections, with: animation) }, completion: closure)
    }
}

// MARK: - UICollectionView reloading functions

extension ReloadCompletable where Self: UICollectionView {

    func reloadSections(_ sections: IndexSet, completion closure: ((Self) -> Void)?) {
        run(transaction: { [weak self] in self?.reloadSections(sections) }, completion: closure)
    }

    func reloadItems(at indexPaths: [IndexPath], completion closure: ((Self) -> Void)?) {
        run(transaction: { [weak self] in self?.reloadItems(at: indexPaths) }, completion: closure)
    }
}

प्रयोग

UITableView

// Activate
extension UITableView: ReloadCompletable { }

// ......
let tableView = UICollectionView()

// reload data
tableView.reloadData { tableView in print(collectionView) }

// or
tableView.reloadRows(at: indexPathsToReload, with: rowAnimation) { tableView in print(tableView) }

// or
tableView.reloadSections(IndexSet(integer: 0), with: rowAnimation) { _tableView in print(tableView) }

UICollectionView

// Activate
extension UICollectionView: ReloadCompletable { }

// ......
let collectionView = UICollectionView()

// reload data
collectionView.reloadData { collectionView in print(collectionView) }

// or
collectionView.reloadItems(at: indexPathsToReload) { collectionView in print(collectionView) }

// or
collectionView.reloadSections(IndexSet(integer: 0)) { collectionView in print(collectionView) }

पूरा नमूना

यहां समाधान कोड जोड़ना न भूलें

import UIKit

class ViewController: UIViewController {

    private weak var navigationBar: UINavigationBar?
    private weak var tableView: UITableView?

    override func viewDidLoad() {
        super.viewDidLoad()
        setupNavigationItem()
        setupTableView()
    }
}
// MARK: - Activate UITableView reloadData with completion functions

extension UITableView: ReloadCompletable { }

// MARK: - Setup(init) subviews

extension ViewController {

    private func setupTableView() {
        guard let navigationBar = navigationBar else { return }
        let tableView = UITableView()
        view.addSubview(tableView)
        tableView.translatesAutoresizingMaskIntoConstraints = false
        tableView.topAnchor.constraint(equalTo: navigationBar.bottomAnchor).isActive = true
        tableView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
        tableView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
        tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
        tableView.dataSource = self
        self.tableView = tableView
    }

    private func setupNavigationItem() {
        let navigationBar = UINavigationBar()
        view.addSubview(navigationBar)
        self.navigationBar = navigationBar
        navigationBar.translatesAutoresizingMaskIntoConstraints = false
        navigationBar.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
        navigationBar.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
        navigationBar.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
        let navigationItem = UINavigationItem()
        navigationItem.rightBarButtonItem = UIBarButtonItem(title: "all", style: .plain, target: self, action: #selector(reloadAllCellsButtonTouchedUpInside(source:)))
        let buttons: [UIBarButtonItem] = [
                                            .init(title: "row", style: .plain, target: self,
                                                  action: #selector(reloadRowButtonTouchedUpInside(source:))),
                                            .init(title: "section", style: .plain, target: self,
                                                  action: #selector(reloadSectionButtonTouchedUpInside(source:)))
                                            ]
        navigationItem.leftBarButtonItems = buttons
        navigationBar.items = [navigationItem]
    }
}

// MARK: - Buttons actions

extension ViewController {

    @objc func reloadAllCellsButtonTouchedUpInside(source: UIBarButtonItem) {
        let elementsName = "Data"
        print("-- Reloading \(elementsName) started")
        tableView?.reloadData { taleView in
            print("-- Reloading \(elementsName) stopped \(taleView)")
        }
    }

    private var randomRowAnimation: UITableView.RowAnimation {
        return UITableView.RowAnimation(rawValue: (0...6).randomElement() ?? 0) ?? UITableView.RowAnimation.automatic
    }

    @objc func reloadRowButtonTouchedUpInside(source: UIBarButtonItem) {
        guard let tableView = tableView else { return }
        let elementsName = "Rows"
        print("-- Reloading \(elementsName) started")
        let indexPathToReload = tableView.indexPathsForVisibleRows?.randomElement() ?? IndexPath(row: 0, section: 0)
        tableView.reloadRows(at: [indexPathToReload], with: randomRowAnimation) { _tableView in
            //print("-- \(taleView)")
            print("-- Reloading \(elementsName) stopped in \(_tableView)")
        }
    }

    @objc func reloadSectionButtonTouchedUpInside(source: UIBarButtonItem) {
        guard let tableView = tableView else { return }
        let elementsName = "Sections"
        print("-- Reloading \(elementsName) started")
        tableView.reloadSections(IndexSet(integer: 0), with: randomRowAnimation) { _tableView in
            //print("-- \(taleView)")
            print("-- Reloading \(elementsName) stopped in \(_tableView)")
        }
    }
}

extension ViewController: UITableViewDataSource {
    func numberOfSections(in tableView: UITableView) -> Int { return 1 }
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 20 }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = UITableViewCell()
        cell.textLabel?.text = "\(Date())"
        return cell
    }
}

परिणाम

यहां छवि विवरण दर्ज करें


0

बस एक अन्य दृष्टिकोण की पेशकश करने के लिए, पूरा होने के विचार के आधार पर 'अंतिम दृश्यमान' सेल को भेजा जाना चाहिए cellForRow

// Will be set when reload is called
var lastIndexPathToDisplay: IndexPath?

typealias ReloadCompletion = ()->Void

var reloadCompletion: ReloadCompletion?

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    // Setup cell

    if indexPath == self.lastIndexPathToDisplay {

        self.lastIndexPathToDisplay = nil

        self.reloadCompletion?()
        self.reloadCompletion = nil
    }

    // Return cell
...

func reloadData(completion: @escaping ReloadCompletion) {

    self.reloadCompletion = completion

    self.mainTable.reloadData()

    self.lastIndexPathToDisplay = self.mainTable.indexPathsForVisibleRows?.last
}

एक संभावित मुद्दा है: यदि reloadData()पहले समाप्त हो गया हैlastIndexPathToDisplay सेट किए जाने गया है, तो सेट 'अंतिम दृश्यमान' सेल प्रदर्शित किया जाएगा lastIndexPathToDisplayऔर पूरा होने पर कॉल नहीं किया जाएगा (और 'प्रतीक्षा' स्थिति में होगा):

self.mainTable.reloadData()

// cellForRowAt could be finished here, before setting `lastIndexPathToDisplay`

self.lastIndexPathToDisplay = self.mainTable.indexPathsForVisibleRows?.last

यदि हम रिवर्स करते हैं, तो हम पहले स्क्रॉल करके पूरा होने के साथ समाप्त हो सकते हैं reloadData()

self.lastIndexPathToDisplay = self.mainTable.indexPathsForVisibleRows?.last

// cellForRowAt could trigger the completion by scrolling here since we arm 'lastIndexPathToDisplay' before 'reloadData()'

self.mainTable.reloadData()

0

इसे इस्तेमाल करे:

tableView.backgroundColor = .black

tableView.reloadData()

DispatchQueue.main.async(execute: {

    tableView.backgroundColor = .green

})

reloadData()फ़ंक्शन पूरा होने के बाद ही तालिका दृश्य का रंग काले से हरे रंग में बदल जाएगा ।


0

आप uitableview के फंक्शनबचअपडेट्स का उपयोग कर सकते हैं

यहाँ आप कैसे प्राप्त कर सकते हैं

self.tableView.performBatchUpdates({

      //Perform reload
        self.tableView.reloadData()
    }) { (completed) in

        //Reload Completed Use your code here
    }

0

CATransaction का पुन: प्रयोज्य विस्तार बनाना:

public extension CATransaction {
    static func perform(method: () -> Void, completion: @escaping () -> Void) {
        begin()
        setCompletionBlock {
            completion()
        }
        method()
        commit()
    }
}

अब UITableView का एक एक्सटेंशन बना रहा है जो CATransaction की एक्सटेंशन विधि का उपयोग करेगा:

public extension UITableView {
    func reloadData(completion: @escaping (() -> Void)) {
       CATransaction.perform(method: {
           reloadData()
       }, completion: completion)
    }
}

उपयोग:

tableView.reloadData(completion: {
    //Do the stuff
})

-2

आप डेटा पुनः लोड करने के बाद कुछ करने के लिए इसका उपयोग कर सकते हैं:

[UIView animateWithDuration:0 animations:^{
    [self.contentTableView reloadData];
} completion:^(BOOL finished) {
    _isUnderwritingUpdate = NO;
}];

-20

विलंब सेट करने का प्रयास करें:

[_tableView performSelector:@selector(reloadData) withObject:nil afterDelay:0.2];
[_activityIndicator performSelector:@selector(stopAnimating) withObject:nil afterDelay:0.2];

14
यह खतरनाक है। क्या होगा अगर आपकी देरी से पुनः लोड होने में अधिक समय लगे?
लूट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.