Dapatkan notifikasi saat UITableView selesai meminta data?

108

Adakah cara untuk mengetahui kapan a UITableViewselesai meminta data dari sumber datanya?

Tak satu pun dari viewDidLoad/ viewWillAppear/ viewDidAppearmetode dari view controller ( UITableViewController) terkait yang digunakan di sini, karena semuanya diaktifkan terlalu dini. Tak satu pun dari mereka (sepenuhnya dapat dimengerti) menjamin bahwa kueri ke sumber data telah selesai untuk saat ini (misalnya, hingga tampilan di-scroll).

Salah satu solusi yang saya temukan adalah untuk memanggil reloadDatadi viewDidAppear, karena, ketika reloadDatakembali, tampilan tabel yang dijamin selesai query sumber data sebanyak itu perlu untuk saat ini.

Namun, ini tampaknya agak buruk, karena saya berasumsi hal itu menyebabkan sumber data dimintai informasi yang sama dua kali (sekali secara otomatis, dan sekali karena reloadDatapanggilan) saat pertama kali dimuat.

Alasan saya ingin melakukan ini semua adalah karena saya ingin mempertahankan posisi gulir UITableView- tetapi sampai ke tingkat piksel, tidak hanya ke baris terdekat.

Saat memulihkan posisi gulir (menggunakan scrollRectToVisible:animated:), saya memerlukan tampilan tabel untuk memiliki data yang cukup di dalamnya, atau scrollRectToVisible:animated:pemanggilan metode tidak melakukan apa pun (yang terjadi jika Anda melakukan panggilan sendiri di salah satu viewDidLoad, viewWillAppearatau viewDidAppear).

kennethmac2000
sumber
Tampaknya Anda mencari sesuatu yang mirip dengan stackoverflow.com/a/11672379/2082172 ini .
Timur Kuchkarov
Dalam pengalaman saya, UITableView dapat menyimpan panggilan ke reloadData, seperti halnya panggilan cache untuk menyisipkan / menghapus baris dan hal lainnya. UITableView akan memanggil delegasi sumber data ketika sudah siap, bergantung pada penggunaan cpu dan hal lainnya.
Penjual Walt

Jawaban:

61

Jawaban ini sepertinya tidak berfungsi lagi, karena beberapa perubahan yang dilakukan pada implementasi UITableView sejak jawabannya ditulis. Lihat komentar ini: Dapatkan pemberitahuan ketika UITableView selesai meminta data?

Aku sudah bermain dengan masalah ini selama beberapa hari dan berpikir bahwa subclassing UITableView's reloadDataadalah pendekatan yang terbaik:

- (void)reloadData {

    NSLog(@"BEGIN reloadData");

    [super reloadData];

    NSLog(@"END reloadData");

}

reloadDatatidak berakhir sebelum tabel selesai memuat ulang datanya. Jadi, ketika yang kedua NSLogdiaktifkan, tampilan tabel sebenarnya telah selesai meminta data.

Saya telah membuat subkelas UITableViewuntuk mengirim metode ke delegasi sebelum dan sesudah reloadData. Ini bekerja seperti pesona.

Eric MORAND
sumber
2
Harus dikatakan, ini sepertinya solusi terbaik. Baru saja menerapkannya dan, seperti yang Anda katakan, itu berfungsi seperti pesona. Terima kasih!
teori
2
@EricMORAND Anda mengatakan bahwa "reloadData tidak berakhir sebelum tabel selesai memuat ulang datanya." Bisakah Anda menjelaskan apa yang Anda maksud dengan itu? Saya menemukan bahwa reloadDatasegera kembali dan saya melihat "END reloadData" sebelum sel benar-benar dimuat ulang (yaitu sebelum UITableViewDataSourcemetode dipanggil). Eksperimen saya menunjukkan kebalikan dari apa yang Anda katakan. Saya harus salah paham tentang apa yang Anda coba katakan.
Rob
3
Bukankah jawaban Eric sama dengan tidak mengimplementasikan (membaca bukan menimpa) reloaddata, memanggil reloaddata (yang pada dasarnya akan menjadi [super reloaddata], dan kemudian setelah memanggilnya, lakukan hal-hal yang Anda inginkan pada penyelesaiannya?
Nirav Bhatt
5
Dahulu kala, reloadData menyelesaikan proses pemuatan sebelum berakhir. Tetapi Apple mengubahnya di beberapa titik. Sekarang kelas UITableView dapat menyimpan panggilan reloadData dengan semua panggilan penyisipan dan penghapusan baris. Jika Anda melihat deklarasi @interface untuk UITableView, Anda akan menemukan anggota NSMutableArray _reloadItems tepat di bawah _insertItems dan _deleteItems. (Saya harus mengerjakan ulang kode yang saya warisi karena perubahan ini.)
Penjual Walt
3
Posting kode selesai pada blok pada antrian utama setelah memanggil [super reloadData]bekerja untuk saya: dispatch_async(dispatch_get_main_queue(), ^{NSLog(@"reload complete");});. Ini pada dasarnya melompati blok yang diposting oleh tampilan tabel reloadData.
Timothy Moose
53

Saya memang memiliki skenario yang sama di aplikasi saya dan berpikir akan memposting jawaban saya kepada kalian karena jawaban lain yang disebutkan di sini tidak berfungsi untuk saya untuk iOS7 dan yang lebih baru

Akhirnya ini adalah satu-satunya hal yang berhasil bagi saya.

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

Pembaruan Swift:

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)

})

Jadi bagaimana ini bekerja.

Pada dasarnya ketika Anda melakukan reload thread utama menjadi sibuk sehingga pada saat kami melakukan pengiriman thread async, blok akan menunggu sampai thread utama selesai. Jadi, setelah tampilan tabel dimuat sepenuhnya, utas utama akan selesai dan karenanya akan mengirimkan blok metode kami

Diuji di iOS7 dan iOS8 dan berfungsi dengan baik;)

Pembaruan untuk iOS9: Ini berfungsi dengan baik untuk iOS9 juga. Saya telah membuat proyek sampel di github sebagai POC. https://github.com/ipraba/TableReloadingNotifier

Saya melampirkan tangkapan layar tes saya di sini.

Lingkungan yang Diuji: Simulator iPhone6 ​​iOS9 dari Xcode7

masukkan deskripsi gambar di sini

ipraba
sumber
8
jawaban terbaik yang saya temukan!
Nikolay Shubenkov
@Gon saya melakukan tes di iOS9 dan itu berfungsi dengan baik. Bisakah Anda merujuk github.com/ipraba/TableReloadingNotifier
ipraba
Ini berfungsi dengan baik di emulator saya tetapi tampaknya tidak berfungsi pada perangkat yang sebenarnya. Adakah orang lain yang mengalami masalah ini?
sosale151
1
Solusi ini akhirnya berhasil untuk saya! ini berfungsi dengan baik di iOS 9
Kuat
1
Saya telah menguji di Real Device, iPhone 6s. Ini bekerja dengan baik juga.
Kuat
25

EDIT: Jawaban ini sebenarnya bukan solusi. Ini mungkin tampak berfungsi pada awalnya karena pemuatan ulang dapat terjadi cukup cepat, tetapi pada kenyataannya blok penyelesaian tidak selalu dipanggil setelah data selesai dimuat ulang - karena reloadData tidak memblokir. Anda mungkin harus mencari solusi yang lebih baik.

Untuk memperluas jawaban @Eric MORAND, mari kita masukkan blok penyelesaian. Siapa yang tidak suka satu blok?

@interface DUTableView : UITableView

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

@end

dan...

#import "DUTableView.h"

@implementation DUTableView

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

@end

Pemakaian:

[self.tableView reloadDataWithCompletion:^{
                                            //do your stuff here
                                        }];
bandejapaisa
sumber
2
Saya tidak bisa mendapatkan cukup blok. Siapa yang butuh delegasi jika Anda punya blok bagus!
bandejapaisa
Saya suka ini, tapi bagaimana dengan saat sistem memanggil reloadData (), misalnya saat tabel pertama kali ditampilkan?
Simetris
12
Ini bukan solusi Blok penyelesaian memulai eksekusi sebelum cellForRowAtIndexPath
zvjerka24
1
Ini tidak akan berhasil; reloadData bukanlah metode pemblokiran. Kode ini dipanggil segera setelah memanggil reloadData, meskipun selnya belum dimuat ulang. Selain itu, lihat kode ini, dan Anda akan melihat bahwa Anda sebaiknya meletakkan kode Anda setelah reloadData .
colinta
1
Ini bekerja untuk saya dengan sedikit perubahan. Alih-alih memanggil blok selesai langsung, menyebutnya dalam blok diposting di antrian utama: dispatch_async(dispatch_get_main_queue(), ^{completionBlock();});. Ini pada dasarnya melompati blok yang diposting oleh tampilan tabel reloadData.
Timothy Moose
11

reloadData hanya meminta data untuk sel yang terlihat. Dikatakan, untuk diberitahu ketika menentukan bagian dari tabel Anda dimuat, silakan kaitkan tableView: willDisplayCell:metode ini.

- (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
}
Black Coder
sumber
1
Saya tidak yakin apakah ini berhasil. Indeks terakhir adalah akhir dari data tabel (misalnya 100 catatan) tetapi tabel hanya akan menampilkan apa yang terlihat di layar (misalnya 8 catatan).
Travis M.
10

Itulah solusi saya. 100% berfungsi dan digunakan di banyak proyek. Ini adalah subkelas UITableView sederhana.

@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

Ini mirip dengan solusi Josh Brown dengan satu pengecualian. Tidak diperlukan penundaan dalam metode performSelector. Tidak peduli berapa lama reloadData. tableViewDidLoadData:selalu aktif saat tableViewselesai bertanya dataSource cellForRowAtIndexPath.

Bahkan jika Anda tidak ingin membuat subkelas, UITableViewAnda cukup memanggil [performSelector:@selector(finishReload) withObject:nil afterDelay:0.0f]dan pemilih Anda akan dipanggil tepat setelah tabel selesai dimuat ulang. Tetapi Anda harus memastikan bahwa pemilih hanya dipanggil sekali per panggilan ke reloadData:

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

Nikmati. :)

Mark Kryzhanouski
sumber
1
Menggunakan panggilan performSelector itu brilian. Sederhana dan bekerja, thanx
Julia
Ini benar-benar luar biasa. Saya telah bertengkar dengan ini selama berhari-hari. Terima kasih!
David Carrico
@MarkKryzhanouski, sudahkah Anda menguji iOS 9?
Victor
@MarkKryzhanouski, terima kasih atas tanggapan yang cepat! Berfungsi dengan baik di iOS 7 dan 8.
Victor
Solusi performSelectoratau menjalankan di utas utama dengan dispatch_asynchtidak berfungsi di iOS 9 .
Manuel
8

Ini adalah jawaban untuk pertanyaan yang sedikit berbeda: Saya perlu tahu kapan UITableViewjuga selesai menelepon cellForRowAtIndexPath(). Saya membuat subkelas layoutSubviews()(terima kasih @Eric MORAND) dan menambahkan panggilan balik delegasi:

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

Pemakaian:

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

CATATAN: Karena ini adalah subclass UITableViewyang sudah memiliki properti delegasi yang menunjuk MyTableViewController, tidak perlu menambahkan yang lain. "Delegasi @dynamic" memberi tahu kompilator untuk menggunakan properti ini. (Berikut tautan yang menjelaskan ini: http://farhadnoorzay.com/2012/01/20/objective-c-how-to-add-delegate-methods-in-a-subclass/ )

The UITableViewproperti di MyTableViewControllerharus diubah untuk menggunakan baru SDTableViewkelas. Ini dilakukan di Interface Builder Identity Inspector. Pilih bagian UITableViewdalam UITableViewControllerdan setel "Kelas Kustom" ke SDTableView.

Simetris
sumber
Di mana Anda menyetel MyTableViewController Anda untuk menjadi delegasi untuk SDTableView? Kenapa mungkin untuk menyetel properti nama "delegate" di SDTableView, ketika superclass-nya sudah memiliki properti dengan nama itu (UITableView.delegate)? Bagaimana Anda mencapai SDTableView kustom Anda ke properti MyTableViewController.tableView jika properti tersebut berjenis "UITablewView" dan objeknya (instance SDTableView) adalah jenis "SDTableView"? Saya melawan masalah yang sama, jadi saya berharap ada solusi untuk masalah ini :)
Earl Grey
Dalam kode Symmetric (SDTableView), bukankah reloadInProgress harus disetel ke FALSE di didReloadData, bukan didLayoutSubviews? Karena reloadData dipanggil setelah layoutSubviews dan pemuatan tidak boleh dianggap selesai sebelum reloadData selesai.
tzuchien.chiu
Ini tidak berfungsi untuk iOS 8, harus memasukkan jawaban iPrabu .
Cantik
6

Saya telah menemukan sesuatu yang mirip untuk mendapatkan notifikasi untuk perubahan contentSizedari TableView. Saya pikir itu seharusnya berfungsi di sini juga karena contentSize juga berubah dengan memuat data.

Coba ini:

Secara viewDidLoadtertulis,

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

dan tambahkan metode ini ke viewController Anda:

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


    }
}

Anda mungkin perlu sedikit modifikasi untuk memeriksa perubahan. Ini berhasil untuk saya.

Bersulang! :)

Abdullah Umer
sumber
1
Sangat pintar. Bekerja dengan sempurna untuk saya. Terima kasih telah berbagi!
DZenBot
2
Anggun! Satu-satunya hal yang ingin saya tambahkan adalah Anda perlu menghapus diri Anda dan pengamat (mungkin dalam metode -dealloc). Atau tambahkan diri Anda sebagai pengamat di -viewWillAppeardan hapus diri Anda dalam -viewWillDisapearmetode.
Loozie
2

Berikut solusi yang mungkin, meskipun ini adalah peretasan:

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

Di mana -scrollTableViewmetode Anda menggulir tampilan tabel dengan -scrollRectToVisible:animated:. Dan, tentu saja, Anda dapat mengonfigurasi penundaan dalam kode di atas dari 0,3 menjadi apa pun yang tampaknya berhasil untuk Anda. Ya, ini sangat meretas, tetapi berfungsi untuk saya di iPhone 5 dan 4S saya ...

Josh Brown
sumber
2
Ini mungkin tampak "hacky", tetapi ini benar-benar pendekatan standar untuk pengguliran: tunda ke siklus proses berikutnya. Saya akan mengubah penundaan menjadi 0, karena itu berfungsi dengan baik dan memiliki lebih sedikit latensi.
phatmann
Tidak berhasil untuk saya dengan penundaan disetel ke 0; 0,3 adalah nilai terendah yang saya bisa dan masih mendapatkan datanya.
Josh Brown
@phatmann Ini berhasil untuk saya. Saya juga bisa menggunakan 0 untuk penundaan saya. Terima kasih untuk kalian berdua.
mcphersonjr
1

Saya memiliki sesuatu yang serupa saya percaya. Saya menambahkan BOOL sebagai variabel instan yang memberi tahu saya jika offset telah dipulihkan dan memeriksanya-viewWillAppear: . Ketika belum dipulihkan, saya mengembalikannya dengan metode itu dan mengatur BOOL untuk menunjukkan bahwa saya memang memulihkan offset.

Ini semacam peretasan dan mungkin bisa dilakukan lebih baik, tetapi ini berfungsi untuk saya saat ini.

Joost
sumber
Ya, masalahnya adalah viewWillAppear tampaknya terlalu dini (setidaknya dalam skenario saya). Mencoba memulihkan offset di viewWillAppear tidak melakukan apa-apa - kecuali saya menambahkan peretasan dengan memanggil reloadData terlebih dahulu. Seperti yang saya pahami, viewWillAppear / viewDidAppear hanya merujuk ke tampilan tabel itu sendiri yang benar-benar muncul - mereka tidak membuat klaim apa pun tentang apakah sel tabel dalam tampilan tersebut telah dihitung atau diisi ... dan ini perlu dilakukan sebelum Anda dapat memulihkan sebuah offset, jika tidak, Anda akan memulihkan offset pada tampilan kosong (dan saya mengerti mengapa hal itu tidak berhasil!).
kennethmac2000
Tapi mungkin yang Anda maksud adalah Anda juga memaksa pemuatan sel tabel terlebih dahulu dengan memanggil reloadData dalam viewWillAppear?
kennethmac2000
Tidak, saya tidak memaksa memuat ulang. Ketika saya mengalami masalah, saya mencoba mengembalikan offset ke dalam -viewDidLoad( di mana seharusnya terjadi tentu saja) tetapi itu hanya berfungsi ketika saya mengatur animasi offset. Dengan memindahkan pengaturan offset agar -viewWillAppear:berfungsi, tetapi saya harus mempertahankan sebuah bendera untuk hanya mengaturnya sekali. Saya kira tampilan tabel memuat ulang datanya setelah ditambahkan ke tampilan, jadi itu sudah masuk -loadView. Apakah Anda yakin data Anda tersedia saat dimuat? Atau sedang dimuat di utas terpisah atau sesuatu?
Joost
Oke, mungkin Anda bisa membantu pemahaman saya di sini. Beginilah cara saya memahami urutan panggilan saat UITableView dibuat. 1) viewDidLoad diaktifkan terlebih dahulu. Ini menunjukkan UITableView dimuat ke dalam memori. 2) viewWillAppear ada di sebelah api. Ini menunjukkan bahwa UITableView akan ditampilkan, tetapi belum tentu semua objek UITableViewCell yang terlihat akan dibuat instance-nya / rendering selesai.
kennethmac2000
2
Dan semua hal di atas benar, pertanyaannya kemudian adalah: opsi non-hacky apa yang kita miliki untuk mencari tahu kapan UITableView telah memperoleh informasi yang cukup dari sumber datanya untuk menjamin permintaan gulir (yaitu, scrollRectToVisible: animated: call ) akan benar-benar berfungsi (dan tidak hanya melakukan apa-apa)?
kennethmac2000
1

Sepertinya Anda ingin memperbarui konten sel, tetapi tanpa lompatan tiba-tiba yang dapat menyertai penyisipan dan penghapusan sel.

Ada beberapa artikel tentang melakukan itu. Ini satu.

Saya sarankan menggunakan setContentOffset: animated: alih-alih scrollRectToVisible: animated: untuk pengaturan pixel-perfect dari tampilan scroll.

Penjual Walt
sumber
1

Anda dapat mencoba logika berikut:

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

Dan sebelum Anda memanggil reloadData, setel prevIndexPath ke nihil. Suka:

prevIndexPath = nil;
[mainTableView reloadData];

Saya menguji dengan NSLogs, dan logika ini tampaknya ok. Anda dapat menyesuaikan / meningkatkan sesuai kebutuhan.

Joe M
sumber
0

akhirnya saya telah membuat kode saya berfungsi dengan ini -

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

ada beberapa hal yang perlu diperhatikan -

  1. sebut dalam " - (UITableViewCell *)MyTableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath"
  2. cukup pastikan bahwa pesan "scrollToRowAtIndexPath" dikirim ke instance UITableView yang relevan, yang dalam kasus ini jelas merupakan MyTableview.
  3. Dalam kasus saya, UIView adalah tampilan yang berisi instance UITableView
  4. Juga, ini akan dipanggil untuk setiap beban sel. Oleh karena itu, letakkan logika di dalam "cellForRowAtIndexPath" untuk menghindari pemanggilan "scrollToRowAtIndexPath" lebih dari sekali.
smile.al.d.way
sumber
dan pastikan bahwa bagian ini tidak dipanggil lebih dari sekali. jika tidak itu mengunci pengguliran.
smile.al.d.way
Ini mungkin berhasil, tetapi jelas tidak elegan dan bukan solusi yang saya cari. Sayangnya, tampaknya tidak ada cara yang lebih baik untuk menentukan apakah -reloadData benar-benar selesai mendapatkan datanya ...
Josh Brown
0

Anda dapat mengubah ukuran tampilan tabel Anda atau menyetelnya ukuran konten dalam metode ini ketika semua data dimuat:

- (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);
}
Banker Mittal
sumber
0

Saya baru saja menjalankan pengatur waktu yang dijadwalkan berulang dan membatalkannya hanya ketika contentSize tabel lebih besar ketika tinggi tableHeaderView (berarti ada konten baris di tabel). Kode di C # (monotouch), tapi saya harap idenya jelas:

    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;
                    }
                }));
        }
    }
Eugene Tiutiunnyk
sumber
0

Tidak UITableView layoutSubviewsdipanggil tepat sebelum tampilan tabel menampilkan isinya? Saya perhatikan bahwa itu dipanggil setelah tampilan tabel selesai memuat datanya, mungkin Anda harus menyelidiki ke arah itu.

Eric MORAND
sumber
0

Sejak iOS 6 dan seterusnya, UITableviewmetode delegasi memanggil:

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

akan dijalankan setelah tabel Anda berhasil dimuat ulang. Anda dapat melakukan kustomisasi sesuai kebutuhan dalam metode ini.

Bharath Vankireddy
sumber
0

Solusi terbaik yang saya temukan di Swift

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

Mengapa tidak diperpanjang saja?

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

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

gulir sampai akhir:

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

Tidak diuji dengan banyak data

Andrii Tishchenko
sumber