UITableView veri istemeyi bitirdiğinde bildirim alın.


108

UITableViewVeri kaynağından veri istemenin ne zaman bittiğini öğrenmenin bir yolu var mı ?

İlişkili görünüm denetleyicisinin viewDidLoad/ viewWillAppear/ viewDidAppearyöntemlerinden hiçbiri (UITableViewController ) burada kullanılmaz, çünkü hepsi çok erken tetiklenir. Bunların hiçbiri (tamamen anlaşılır bir şekilde), veri kaynağına yapılan sorguların şimdilik (örneğin, görünüm kaydırılıncaya kadar) bittiğini garanti etmez.

Bir buldum çağırmaktır var Geçici çözüm reloadDatade viewDidAppearzaman, çünkü reloadDatadöner, tablo görünümünde olduğu zamanı varlık için için ihtiyacı olduğu gibi veri kaynağını sorgulama bitirdikten garantilidir.

Bununla birlikte, veri kaynağından aynı bilgilerin iki kez sorulmasına neden olduğunu varsaydığım için bu oldukça çirkin görünüyor (bir kez otomatik olarak ve bir kez reloadData ilk yüklendiğinde arama ) .

Bunu kesinlikle yapmak istememin nedeni, kaydırma konumunu korumak istememdir UITableView- ancak sadece en yakın satıra değil, piksel seviyesine kadar.

Kaydırma konumu (kullanarak geri yüklerken scrollRectToVisible:animated:), Ben zaten yeterli veriye sahip çocukları ya da tablo görünümü gerek scrollRectToVisible:animated:yöntem çağrısı (eğer herhangi kendi başına aramayı eğer böyle olur hiçbir şey yapmaz viewDidLoad, viewWillAppearya da viewDidAppear).


Görünüşe göre bu stackoverflow.com/a/11672379/2082172'ye benzer bir şey arıyorsunuz .
Timur Kuchkarov

Tecrübelerime göre, UITableView, satırları ve diğer şeyleri eklemek / silmek için çağrıları önbelleğe aldığı gibi, reloadData çağrılarını önbelleğe alabilir. UITableView, hazır olduğunda, cpu kullanımına ve diğer şeylere bağlı olarak veri kaynağı temsilcisini çağırır.
Walt Sellers

Yanıtlar:


61

Cevap yazıldıktan sonra UITableView uygulamasında yapılan bazı değişiklikler nedeniyle, bu yanıt artık işe yaramıyor gibi görünüyor. Bu yoruma bakın: UITableView veri istemeyi bitirdiğinde bildirim alın?

Ben birkaç gün boyunca bu sorunla oynayan ve o subclassing düşünmek oldum UITableView'in reloadDataen iyi yaklaşımdır:

- (void)reloadData {

    NSLog(@"BEGIN reloadData");

    [super reloadData];

    NSLog(@"END reloadData");

}

reloadDatatablonun verilerini yeniden yüklemesi bitmeden bitmez. Bu nedenle, ikincisi çalıştırıldığında NSLog, tablo görünümü aslında veri istemeyi bitirmiştir.

UITableViewTemsilciye önce ve sonra yöntemler göndermek için alt sınıflara girdim reloadData. Mucizevi şekilde çalışır.


2
Söylemeliyim, bu en iyi çözüm gibi görünüyor. Sadece uyguladı ve sizin de dediğiniz gibi bir cazibe gibi çalışıyor. Teşekkürler!
teori

2
@EricMORAND "reloadData, tablonun verilerini yeniden yüklemeyi bitirmeden bitmez" diyorsunuz. Bununla ne demek istediğini açıklayabilir misin? Bunu bulmak reloadDatahemen döner ve "END reloadData" bakın önce hücreler aslında (yani önce yeniden yüklenir UITableViewDataSourceyöntemleri denir). Benim deneyim, söylediklerinizin tam tersini gösteriyor. Söylemeye çalıştığını yanlış anlamalıyım.
Rob

3
Eric'in cevabı, yeniden yükleme verilerini uygulamama (geçersiz kılma değil okuma), yeniden yükleme verilerini çağırma (ki bu esasen [süper yeniden yükleme verileri] olacak ve sonra onu çağırdıktan sonra, tamamlandığında istediğiniz şeyleri yapacak mı?
Nirav Bhatt

5
Bir zamanlar reloadData, yükleme işlemini bitmeden tamamladı. Ancak Apple bunu bir noktada değiştirdi. Artık UITableView sınıfı, reloadData çağrısını tüm satır ekleme ve silme çağrılarıyla önbelleğe alabilir. UITableView için @interface bildirimine bakarsanız, NSMutableArray üyesini _reloadItems, _insertItems ve _deleteItems altında bulacaksınız. (Bu değişiklik nedeniyle miras aldığım kodu yeniden işlemek zorunda kaldım.)
Walt Sellers

3
Çağırdıktan sonra ana sırasına bir blokta tamamlama kodunu Gönderme [super reloadData]benim için işler: dispatch_async(dispatch_get_main_queue(), ^{NSLog(@"reload complete");});. Temelde, tablo görünümü tarafından gönderilen blokları atlar reloadData.
Timothy Moose

53

Uygulamamda da aynı bir senaryoya sahiptim ve cevabımı size göndereceğimi düşündüm, çünkü burada bahsedilen diğer cevaplar iOS7 ve sonrası için benim için çalışmıyor

Sonunda benim için işe yarayan tek şey buydu.

[yourTableview reloadData];

dispatch_async(dispatch_get_main_queue(),^{
        NSIndexPath *path = [NSIndexPath indexPathForRow:yourRow inSection:yourSection];
        //Basically maintain your logic to get the indexpath
        [yourTableview scrollToRowAtIndexPath:path atScrollPosition:UITableViewScrollPositionTop animated:YES];
 });

Swift Güncellemesi:

yourTableview.reloadData()
dispatch_async(dispatch_get_main_queue(), { () -> Void in
    let path : NSIndexPath = NSIndexPath(forRow: myRowValue, inSection: mySectionValue)
    //Basically maintain your logic to get the indexpath
    yourTableview.scrollToRowAtIndexPath(path, atScrollPosition: UITableViewScrollPosition.Top, animated: true)

})

Peki bu nasıl işliyor.

Temel olarak, bir yeniden yükleme yaptığınızda ana iş parçacığı meşgul olur, bu nedenle eşzamansız bir iş parçacığı gönderirken, blok ana iş parçacığı bitene kadar bekleyecektir. Dolayısıyla, tablo görünümü tamamen yüklendikten sonra ana iş parçacığı bitecek ve böylece yöntem bloğumuzu gönderecektir.

İOS7 ve iOS8'de test edildi ve harika çalışıyor;)

İOS9 için güncelleme: Bu sadece iyi çalışıyor iOS9 da. Github'da POC olarak örnek bir proje oluşturdum. https://github.com/ipraba/TableReloadingNotifier

Testimin ekran görüntüsünü buraya ekliyorum.

Test Edilen Ortam: Xcode7'den iOS9 iPhone6 ​​simülatörü

görüntü açıklamasını buraya girin


8
bulduğum en iyi cevap!
Nikolay Shubenkov

@Gon iOS9'da bir test yaptım ve gayet iyi çalışıyor. Github.com/ipraba/TableReloadingNotifier
ipraba

Öykünücümde iyi çalışıyor ancak gerçek cihazda çalışmıyor gibi görünüyor. Bu sorunu yaşayan başka biri var mı?
sosale151

1
Bu Çözüm nihayet benim için çalışıyor! iOS 9'da sorunsuz çalışıyor
Güçlü

1
Bir iPhone 6s olan Real Device üzerinde test ettim. O da iyi çalışıyor.
Güçlü

25

DÜZENLEME: Bu cevap aslında bir çözüm değil. Muhtemelen ilk başta işe yarıyor gibi görünüyor çünkü yeniden yükleme oldukça hızlı gerçekleşebilir, ancak aslında tamamlama bloğu, verilerin yeniden yüklenmesi tamamen bittikten sonra mutlaka çağrılmaz - çünkü reloadData engellemez. Muhtemelen daha iyi bir çözüm aramalısınız.

@Eric MORAND'ın cevabını genişletmek için, bir tamamlama bloğu koyalım. Kim bir bloğu sevmez?

@interface DUTableView : UITableView

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

@end

ve...

#import "DUTableView.h"

@implementation DUTableView

- (void) reloadDataWithCompletion:( void (^) (void) )completionBlock {
    [super reloadData];
    if(completionBlock) {
        completionBlock();
    }
}

@end

Kullanımı:

[self.tableView reloadDataWithCompletion:^{
                                            //do your stuff here
                                        }];

2
Yeterince blok alamıyorum. Güzel bir bloğunuz olduğunda bir temsilciye kimin ihtiyacı var!
bandejapaisa

Bunu beğendim, ama sistemin reloadData () 'yı çağırdığı zamanlar, örneğin tablo ilk görüntülendiğinde ne olacak?
Simetrik

12
Bu bir çözüm değil Tamamlama bloğu, cellForRowAtIndexPath'ten önce yürütmeye başlar
zvjerka24

1
Bu işe yaramayacak; reloadData bir engelleme yöntemi değildir. Bu kod, hücreler henüz yeniden yüklenmemiş olsa bile reloadData çağrıldıktan hemen sonra çağrılır . Ayrıca, bu koda bakın ve göreceksiniz ki , kodunuzu reloadData'dan sonra da koyabilirsiniz .
colinta

1
Bu benim için küçük bir değişiklikle çalışıyor. Bunun yerine, doğrudan tamamlama bloğu çağırmak, ana kuyruğuna yayınlanan bir blokta diyoruz: dispatch_async(dispatch_get_main_queue(), ^{completionBlock();});. Bu temelde, tablo görünümünün gönderdiği blokları atlar reloadData.
Timothy Moose

11

reloadData sadece görünen hücreler için veri ister. Diyor ki, tablonuzun belirli bir bölümü yüklendiğinde haberdar olmak için, lütfen tableView: willDisplayCell:yöntemi bağlayın.

- (void) reloadDisplayData
{
    isLoading =  YES;
    NSLog(@"Reload display with last index %d", lastIndex);
    [_tableView reloadData];
    if(lastIndex <= 0){
    isLoading = YES;
    //Notify completed
}

- (void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if(indexPath.row >= lastIndex){
    isLoading = NO;
    //Notify completed
}

1
Bunun işe yarayıp yaramadığından emin değilim. Son indeks, tablo verilerinin sonudur (örn. 100 kayıt), ancak tablo yalnızca ekranda görüneni gösterecektir (örn. 8 kayıt).
Travis M.

10

Benim çözümüm bu. % 100 çalışır ve birçok projede kullanılır. Basit bir UITableView alt sınıfıdır.

@protocol MyTableViewDelegate<NSObject, UITableViewDelegate>
@optional
- (void)tableViewWillReloadData:(UITableView *)tableView;
- (void)tableViewDidReloadData:(UITableView *)tableView;
@end

@interface MyTableView : UITableView {
    struct {
        unsigned int delegateWillReloadData:1;
        unsigned int delegateDidReloadData:1;
        unsigned int reloading:1;
    } _flags;
}
@end

@implementation MyTableView
- (id<MyTableViewDelegate>)delegate {
    return (id<MyTableViewDelegate>)[super delegate];
}

- (void)setDelegate:(id<MyTableViewDelegate>)delegate {
    [super setDelegate:delegate];
    _flags.delegateWillReloadData = [delegate respondsToSelector:@selector(tableViewWillReloadData:)];
    _flags.delegateDidReloadData = [delegate    respondsToSelector:@selector(tableViewDidReloadData:)];
}

- (void)reloadData {
    [super reloadData];
    if (_flags.reloading == NO) {
        _flags.reloading = YES;
        if (_flags.delegateWillReloadData) {
            [(id<MyTableViewDelegate>)self.delegate tableViewWillReloadData:self];
        }
        [self performSelector:@selector(finishReload) withObject:nil afterDelay:0.0f];
    }
}

- (void)finishReload {
    _flags.reloading = NO;
    if (_flags.delegateDidReloadData) {
        [(id<MyTableViewDelegate>)self.delegate tableViewDidReloadData:self];
    }
}

@end

Bu benzeyen Josh Brown solüsyonu bir istisna dışında. PerformSelector yönteminde gecikmeye gerek yoktur. Ne kadar uzun reloadDatasürerse sürsün. tableViewDidLoadData:her zaman tableViewsormayı bitirdiğinde ateşler dataSource cellForRowAtIndexPath.

Alt sınıfa geçmek istemeseniz bile UITableView, basitçe arayabilir [performSelector:@selector(finishReload) withObject:nil afterDelay:0.0f]ve seçiciniz masanın yeniden yüklenmesi bittikten hemen sonra çağrılır. Ancak, seçicinin her arama için yalnızca bir kez arandığından emin olmalısınız reloadData:

[self.tableView reloadData];
[self performSelector:@selector(finishReload) withObject:nil afterDelay:0.0f];

Zevk almak. :)


1
PerformSelector çağrısını kullanmak harika. Basit ve çalışıyor, teşekkürler
Julia

Bu kesinlikle harika. Bununla günlerdir boğuşuyorum. Teşekkür ederim!
David Carrico

@MarkKryzhanouski, iOS 9'da test ettiniz mi?
Victor

@MarkKryzhanouski, hızlı geri bildirim için teşekkürler! İOS 7 ve 8'de harika çalışıyor.
Victor

Çözümler performSelectorveya ana iş parçacığı üzerinde çalıştırma iOS 9'dadispatch_asynch çalışmaz .
Manuel

8

Bu, biraz farklı bir sorunun cevabı: Aramayı ne zaman UITableViewbitirdiğimi de bilmem gerekiyordu cellForRowAtIndexPath(). Alt sınıflandırdım layoutSubviews()(teşekkürler @Eric MORAND) ve bir temsilci geri araması ekledim:

SDTableView.h:

@protocol SDTableViewDelegate <NSObject, UITableViewDelegate>
@required
- (void)willReloadData;
- (void)didReloadData;
- (void)willLayoutSubviews;
- (void)didLayoutSubviews;
@end

@interface SDTableView : UITableView

@property(nonatomic,assign) id <SDTableViewDelegate> delegate;

@end;

SDTableView.m:

#import "SDTableView.h"

@implementation SDTableView

@dynamic delegate;

- (void) reloadData {
    [self.delegate willReloadData];

    [super reloadData];

    [self.delegate didReloadData];
}

- (void) layoutSubviews {
    [self.delegate willLayoutSubviews];

    [super layoutSubviews];

    [self.delegate didLayoutSubviews];
}

@end

Kullanımı:

MyTableViewController.h:

#import "SDTableView.h"
@interface MyTableViewController : UITableViewController <SDTableViewDelegate>
@property (nonatomic) BOOL reloadInProgress;

MyTableViewController.m:

#import "MyTableViewController.h"
@implementation MyTableViewController
@synthesize reloadInProgress;

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {

    if ( ! reloadInProgress) {
        NSLog(@"---- numberOfSectionsInTableView(): reloadInProgress=TRUE");
        reloadInProgress = TRUE;
    }

    return 1;
}

- (void)willReloadData {}
- (void)didReloadData {}
- (void)willLayoutSubviews {}

- (void)didLayoutSubviews {
    if (reloadInProgress) {
        NSLog(@"---- layoutSubviewsEnd(): reloadInProgress=FALSE");
        reloadInProgress = FALSE;
    }
}

NOTLAR: Bu, bir alt sınıf olduğundan, UITableViewdelege özelliğine işaret eden MyTableViewControllerbaşka bir tane eklemeye gerek yoktur. "@Dinamik temsilci" derleyiciye bu özelliği kullanmasını söyler. (Bunu açıklayan bir bağlantı burada: http://farhadnoorzay.com/2012/01/20/objective-c-how-to-add-delegate-methods-in-a-subclass/ )

Yeni sınıfı kullanmak için içindeki UITableViewözellik MyTableViewControllerdeğiştirilmelidir SDTableView. Bu, Interface Builder Identity Inspector'da yapılır. UITableViewİçini seçin ve UITableViewController"Özel Sınıf" olarak ayarlayın SDTableView.


MyTableViewController'ınızı SDTableView için temsilci olarak nerede ayarlarsınız? SDTableView üzerinde "temsilci" adının bir özelliğini, üst sınıfının zaten bu adın bir özelliğine (UITableView.delegate) sahip olması durumunda ayarlamak nasıl mümkün oluyor? Özellik "UITablewView" türünde olduğunda ve nesne (SDTableView örneği) "SDTableView" türünde olduğunda, özel SDTableView'ınızı MyTableViewController.tableView özelliğine nasıl eklersiniz? Ben de aynı sorunla mücadele ediyorum, bu yüzden bu sorunlara bir çözüm
Earl Grey

Symmetric (SDTableView) kodunda reloadInProgress, didLayoutSubviews yerine didReloadData'da FALSE olarak ayarlanmamalıdır? Çünkü reloadData, layoutSubviews'den sonra çağrılır ve yükleme, reloadData bitmeden önce yapılmamalıdır.
tzuchien.chiu

Bu iOS 8 için çalışmıyor, iPrabu'nun cevabını dahil etmek zorundaydı .
Stunner

6

Ben değişim için bildirim almak için benzer bir şey bulduğunu contentSizearasında TableView. Bunun burada da çalışması gerektiğini düşünüyorum, çünkü contentSize ayrıca veri yüklerken de değişiyor.

Bunu dene:

In viewDidLoadyazma,

[self.tableView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld | NSKeyValueObservingOptionPrior context:NULL];

ve bu yöntemi viewController'ınıza ekleyin:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if ([keyPath isEqualToString:@"contentSize"]) {
        DLog(@"change = %@", change.description)

        NSValue *new = [change valueForKey:@"new"];
        NSValue *old = [change valueForKey:@"old"];

        if (new && old) {
            if (![old isEqualToValue:new]) {
                // do your stuff
            }
        }


    }
}

Değişiklik kontrolünde küçük değişikliklere ihtiyacınız olabilir. Bu benim için işe yaradı.

Şerefe! :)


1
Çok zeki. Benim için mükemmel çalışıyor. Paylaşım için teşekkürler!
DZenBot

2
Zarif! Ekleyeceğim tek şey, kendinizi ve bir gözlemciyi kaldırmanız gerektiğidir (belki de -dealloc yönteminde). Ya da kendinizi bir gözlemci olarak ekleyin -viewWillAppearve -viewWillDisapearyöntemde kendinizi kaldırın .
Loozie

2

İşte bir hack olsa da olası bir çözüm:

[self.tableView reloadData];
[self performSelector:@selector(scrollTableView) withObject:nil afterDelay:0.3];

Nerede -scrollTableViewyöntemi ile tablo görünümü kayar -scrollRectToVisible:animated:. Ve tabii ki, yukarıdaki koddaki gecikmeyi 0.3'ten sizin için işe yarayan herhangi bir şeye kadar yapılandırabilirsiniz. Evet, gülünç derecede karmaşık, ama benim için iPhone 5 ve 4S'de çalışıyor ...


2
"Keskin" görünebilir, ancak bu gerçekten kaydırma için standart bir yaklaşımdır: bir sonraki çalıştırma döngüsüne erteleyin. Gecikmeyi 0 olarak değiştirirdim, çünkü bu da işe yarıyor ve daha az gecikme süresine sahip.
phatmann

0 olarak ayarlanmış gecikme ile benim için işe yaramadı; 0.3, gidebildiğim ve hala verileri alabildiğim en düşük seviyeydi.
Josh Brown

@phatmann Bu benim için çalıştı. Ayrıca gecikmem için 0 kullanabildim. İkinize de teşekkürler.
mcphersonjr

1

İnandığım benzer bir şey vardı. Örnek değişkeni olarak, ofsetin geri yüklenip yüklenmediğini ve bunu kontrol edip etmediğini söyleyen bir BOOL ekledim -viewWillAppear:. Geri yüklenmediğinde, bu yöntemle geri yüklerim ve BOOL'u ofseti kurtardığımı belirtecek şekilde ayarlıyorum.

Bu bir tür hack ve muhtemelen daha iyi yapılabilir, ancak bu şu anda benim için çalışıyor.


Evet, sorun şu ki viewWillAppear çok erken görünüyor (en azından benim senaryomda). ViewWillAppear'da göreli konumu geri yüklemeye çalışmak, önce reloadData'yı çağırma hackini eklemediğim sürece hiçbir şey yapmıyor. Anladığım kadarıyla, viewWillAppear / viewDidAppear yalnızca tablo görünümünün kendisinin gerçekte göründüğüne atıfta bulunuyor - görünümdeki tablo hücrelerinin numaralandırılmış veya doldurulmuş olup olmadığı konusunda herhangi bir iddiada bulunmazlar ... ve bunun siz kurtarmadan önce gerçekleşmesi gerekir bir ofset, aksi takdirde boş bir görünümde bir ofseti kurtarırsınız (ve bunun neden işe yaramayacağını anlıyorum!).
kennethmac2000

Ama belki de ilk olarak tablo hücrelerinin yüklenmesini viewWillAppear içinde reloadData'yı çağırarak zorladığınızı kastediyorsunuz?
kennethmac2000

Hayır, yeniden yüklemeye zorlamıyorum. Sorun yaşadığımda, ofseti geri yüklemeye çalıştım -viewDidLoad(tabii ki olması gerektiği yerde), ancak bu yalnızca ofseti animasyonlu olarak ayarladığımda işe yaradı. Ofset ayarını -viewWillAppear:ona taşıyarak işe yaradı, ancak yalnızca bir kez ayarlamak için bir bayrak tutmam gerekiyordu. Sanırım tablo görünümü, bir görünüme eklendikten sonra verilerini yeniden yüklüyor, yani zaten var -loadView. Verilerinizin görüntüleme yükünde mevcut olduğundan emin misiniz? Yoksa ayrı bir iş parçacığına veya başka bir şeye mi yükleniyor?
Joost

Tamam, belki burada anlamama yardımcı olabilirsiniz. Bir UITableView oluşturulduğunda çağrıların sırasını bu şekilde anlıyorum. 1) Önce viewDidLoad tetiklenir. Bu, UITableView'ün belleğe yüklendiğini gösterir. 2) viewWillAppear ateşin yanında. Bu, UITableView'ün görüntüleneceğini, ancak tüm görünür UITableViewCell nesnelerinin tam olarak somutlaştırılacağını / tamamlandığını belirtir.
kennethmac2000

2
Ve yukarıdakilerin tümü doğrudur, o zaman soru şudur: UITableView bir kaydırma isteğini garanti etmek için veri kaynağından yeterli bilgi aldığında (yani, bir scrollRectToVisible: animated: call) bulmak için hangi hack olmayan seçeneklere sahibiz? ) gerçekten çalışacak (ve sadece hiçbir şey yapmayacak)?
kennethmac2000

1

Hücre içeriğini güncellemek istiyormuşsunuz gibi görünüyor, ancak hücre eklemelerine ve silmelerine eşlik edebilecek ani atlamalar olmadan.

Bunu yapmakla ilgili birkaç makale var. Bu bir.

Bir kaydırma görünümünün piksel açısından mükemmel ayarları için scrollRectToVisible: animated: yerine setContentOffset: animated: kullanmanızı öneririm.


1

Aşağıdaki mantığı deneyebilirsiniz:

-(UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MyIdentifier"];

    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"MyIdentifier"];
        cell.selectionStyle = UITableViewCellSelectionStyleNone;
    }

    if ( [self chkIfLastCellIndexToCreate:tableView :indexPath]){
        NSLog(@"Created Last Cell. IndexPath = %@", indexPath);
        //[self.activityIndicator hide];
        //Do the task for TableView Loading Finished
    }
    prevIndexPath = indexPath;

    return cell;
}



-(BOOL) chkIfLastCellIndexToCreate:(UITableView*)tableView : (NSIndexPath *)indexPath{

    BOOL bRetVal = NO;
    NSArray *visibleIndices = [tableView indexPathsForVisibleRows];

    if (!visibleIndices || ![visibleIndices count])
        bRetVal = YES;

    NSIndexPath *firstVisibleIP = [visibleIndices objectAtIndex:0];
    NSIndexPath *lastVisibleIP = [visibleIndices objectAtIndex:[visibleIndices count]-1];

    if ((indexPath.row > prevIndexPath.row) && (indexPath.section >= prevIndexPath.section)){
        //Ascending - scrolling up
        if ([indexPath isEqual:lastVisibleIP]) {
            bRetVal = YES;
            //NSLog(@"Last Loading Cell :: %@", indexPath);
        }
    } else if ((indexPath.row < prevIndexPath.row) && (indexPath.section <= prevIndexPath.section)) {
        //Descending - scrolling down
        if ([indexPath isEqual:firstVisibleIP]) {
            bRetVal = YES;
            //NSLog(@"Last Loading Cell :: %@", indexPath);
        }
    }
    return bRetVal;
}

ReloadData öğesini çağırmadan önce prevIndexPath öğesini nil olarak ayarlayın. Sevmek:

prevIndexPath = nil;
[mainTableView reloadData];

NSLogs ile test ettim ve bu mantık iyi görünüyor. Gerektiği gibi özelleştirebilir / geliştirebilirsiniz.


0

sonunda kodumun bununla çalışmasını sağladım -

[tableView scrollToRowAtIndexPath:scrollToIndex atScrollPosition:UITableViewScrollPositionTop animated:YES];

halledilmesi gereken birkaç şey vardı -

  1. " - (UITableViewCell *)MyTableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath" içinde ara
  2. sadece "scrollToRowAtIndexPath" mesajının, bu durumda kesinlikle MyTableview olan UITableView'in ilgili örneğine gönderildiğinden emin olun.
  3. Benim durumumda UIView, UITableView örneğini içeren görünümdür
  4. Ayrıca, bu her hücre yükü için çağrılacaktır. Bu nedenle, "scrollToRowAtIndexPath" öğesini birden çok kez çağırmamak için "cellForRowAtIndexPath" içine bir mantık koyun.

ve sadece bu parçanın birden fazla çağrılmamasını sağlayın. değilse kaydırmayı kilitler.
smile.al.d.way

Bu işe yarayabilir, ancak kesinlikle şık değil ve aradığım çözüm değil. Ne yazık ki, -reloadData'nın verileri almayı gerçekten bitirip bitirmediğini belirlemenin daha iyi bir yolu yok gibi görünüyor ...
Josh Brown

0

Tüm veriler yüklendiğinde bu yöntemde tablo görünümünüzü yeniden boyutlandırabilir veya içerik boyutunu ayarlayabilirsiniz:

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    tableView.frame =CGRectMake(tableView.frame.origin.x, tableView.frame.origin.y, tableView.frame.size.width, tableView.contentSize.height);
}

0

Yinelenen zamanlayıcıyı çalıştırıyorum ve yalnızca tableHeaderView yüksekliği olduğunda tablonun contentSize daha büyük olduğunda geçersiz kılıyorum (tabloda satır içeriği olduğu anlamına gelir). C # kodunda (monotouch), ancak umarım fikir açıktır:

    public override void ReloadTableData()
    {
        base.ReloadTableData();

        // don't do anything if there is no data
        if (ItemsSource != null && ItemsSource.Length > 0)
        {
            _timer = NSTimer.CreateRepeatingScheduledTimer(TimeSpan.MinValue, 
                new NSAction(() => 
                {
                    // make sure that table has header view and content size is big enought
                    if (TableView.TableHeaderView != null &&
                        TableView.ContentSize.Height > 
                            TableView.TableHeaderView.Frame.Height)
                    {
                        TableView.SetContentOffset(
                            new PointF(0, TableView.TableHeaderView.Frame.Height), false);
                        _timer.Invalidate();
                        _timer = null;
                    }
                }));
        }
    }

0

UITableView layoutSubviewsTablo görünümü içeriğini görüntülemeden hemen önce çağrılmaz mı ? Tablo görünümü verilerini yüklemeyi bitirdiğinde çağrıldığını fark ettim, belki bu yönde araştırmalısınız.


0

İOS 6'dan itibaren UITableviewdelege yöntemi şunları çağırdı:

-(void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section

tablonuz başarıyla yeniden yüklendiğinde çalıştırılacaktır. Bu yöntemde gerektiği gibi özelleştirme yapabilirsiniz.


0

Swift'de bulduğum en iyi çözüm

extension UITableView {
    func reloadData(completion: ()->()) {
        self.reloadData()
        dispatch_async(dispatch_get_main_queue()) {
            completion()
        }
    }
}

-1

Neden uzatmıyorsun?

@interface UITableView(reloadComplete)
- (void) reloadDataWithCompletion:( void (^) (void) )completionBlock;
@end

@implementation UITableView(reloadComplete)
- (void) reloadDataWithCompletion:( void (^) (void) )completionBlock {
    [self reloadData];
    if(completionBlock) {
        completionBlock();
    }
}
@end

sona kaydır:

[self.table reloadDataWithCompletion:^{
    NSInteger numberOfRows = [self.table numberOfRowsInSection:0];
    if (numberOfRows > 0)
    {
        NSIndexPath *indexPath = [NSIndexPath indexPathForRow:numberOfRows-1 inSection:0];
        [self.table scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:NO];
    }
}];

Çok fazla veri ile test edilmedi

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.