Bagaimana cara mendeteksi bahwa animasi telah berakhir di UITableView beginUpdates / endUpdates?

116

Saya memasukkan / menghapus sel tabel menggunakan insertRowsAtIndexPaths/deleteRowsAtIndexPathsdibungkus beginUpdates/endUpdates. Saya juga menggunakan beginUpdates/endUpdatessaat menyesuaikan rowHeight. Semua operasi ini dianimasikan secara default.

Bagaimana saya bisa mendeteksi bahwa animasi telah berakhir saat menggunakan beginUpdates/endUpdates?

pixelfreak
sumber
1
FYI: Ini juga berlaku untuk -scrollToRowAtIndexPath:atScrollPosition:animated:.
Ben Lachman

Jawaban:

292

Bagaimana dengan ini?

[CATransaction begin];

[CATransaction setCompletionBlock:^{
    // animation has finished
}];

[tableView beginUpdates];
// do some work
[tableView endUpdates];

[CATransaction commit];

Ini berfungsi karena animasi tableView menggunakan CALayeranimasi secara internal. Artinya, mereka menambahkan animasi ke sembarang tempat CATransaction. Jika tidak CATransactionada terbuka (kasus normal), maka satu secara implisit dimulai, yang diakhiri pada akhir runloop saat ini. Tetapi jika Anda memulainya sendiri, seperti yang dilakukan di sini, maka itu akan menggunakan yang itu.

Rudolf Adamkovič
sumber
7
Tidak berhasil untuk saya. Blok penyelesaian dipanggil saat animasi masih berjalan.
mrvincenzo
2
@MrVincenzo Apakah Anda mengatur completionBlocksebelumnya beginUpdatesdan endUpdatessuka di cuplikan?
Rudolf Adamkovič
2
[CATransaction commit]harus dipanggil setelah bukan sebelumnya [tableView endUpdates].
Rudolf Adamkovič
6
Blok penyelesaian tidak pernah dipanggil jika sel berisi tampilan dengan animasi, yaitu UIActivityIndicatorView.
markturnip
3
Setelah sekian lama aku tidak pernah tahu ini. Emas murni !! Tidak ada yang lebih buruk daripada melihat animasi bagus terbunuh dengan kejahatan yang diperlukan dari tableView.reloadData ().
DogCoffee
31

Versi Swift


CATransaction.begin()

CATransaction.setCompletionBlock({
    do.something()
})

tableView.beginUpdates()
tableView.endUpdates()

CATransaction.commit()
Michael
sumber
6

Jika Anda menargetkan iOS 11 dan yang lebih baru, Anda harus menggunakan UITableView.performBatchUpdates(_:completion:):

tableView.performBatchUpdates({
    // delete some cells
    // insert some cells
}, completion: { finished in
    // animation complete
})
Robert
sumber
5

Solusi yang mungkin adalah mewarisi dari UITableView tempat Anda memanggil endUpdatesdan menimpanya setContentSizeMethod, karena UITableView menyesuaikan ukuran kontennya agar cocok dengan baris yang ditambahkan atau dihapus. Pendekatan ini juga harus berhasil reloadData.

Untuk memastikan bahwa notifikasi dikirim hanya setelah endUpdatesdipanggil, seseorang juga bisa menimpa endUpdatesdan menyetel bendera di sana.

// somewhere in header
@private BOOL endUpdatesWasCalled_;

-------------------

// in implementation file

- (void)endUpdates {
    [super endUpdates];
    endUpdatesWasCalled_ = YES;
}

- (void)setContentSize:(CGSize)contentSize {
    [super setContentSize:contentSize];

    if (endUpdatesWasCalled_) {
        [self notifyEndUpdatesFinished];
        endUpdatesWasCalled_ = NO;
    }
}
Antoni
sumber
5

Anda dapat memasukkan operasi Anda dalam blok animasi UIView seperti ini:

- (void)tableView:(UITableView *)tableView performOperation:(void(^)())operation completion:(void(^)(BOOL finished))completion
{
    [UIView animateWithDuration:0.0 animations:^{

        [tableView beginUpdates];
        if (operation)
            operation();
        [tableView endUpdates];

    } completion:^(BOOL finished) {

        if (completion)
            completion(finished);
    }];
}

Penghargaan untuk https://stackoverflow.com/a/12905114/634940 .

Zdenek
sumber
4
Kode ini tidak berhasil untuk saya. Blok penyelesaian dipanggil setelahnya endUpdates, tetapi sebelum animasi selesai.
Tim Camber
1
Ini tidak berhasil. Animasi tampilan tabel berhenti bekerja, saat menerapkan contoh ini.
Primoz990
Coba buat durasinya lebih lama (seperti 0,3 detik) - seharusnya berhasil (berhasil untuk saya)
emem
1

Belum menemukan solusi yang baik (singkatnya subclassing UITableView). Saya telah memutuskan untuk menggunakan performSelector:withObject:afterDelay:untuk saat ini. Tidak ideal, tetapi menyelesaikan pekerjaan.

UPDATE : Sepertinya saya dapat menggunakan scrollViewDidEndScrollingAnimation:untuk tujuan ini (ini khusus untuk implementasi saya, lihat komentar).

pixelfreak
sumber
1
scrollViewDidEndScrollingAnimationhanya dipanggil untuk menanggapi setContentOffsetdanscrollRectToVisible
samvermette
@samvermette Nah, dalam kasus saya, ketika beginUpdates / endUpdates dipanggil, UITableView selalu menganimasikan-scroll. Tetapi ini khusus untuk penerapan saya, jadi saya setuju ini bukan jawaban terbaik tetapi berhasil untuk saya.
pixelfreak
Tampaknya dimungkinkan untuk menyertakan pembaruan ke dalam blok animasi UIView. Lihat jawaban saya di bawah: stackoverflow.com/a/14305039/634940 .
Zdenek
0

Anda bisa menggunakan tableView:willDisplayCell:forRowAtIndexPath:seperti:

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
    NSLog(@"tableView willDisplay Cell");
    cell.backgroundColor = [UIColor colorWithWhite:((indexPath.row % 2) ? 0.25 : 0) alpha:0.70];
}

Tapi ini juga akan dipanggil saat sel yang sudah ada di tabel berpindah dari layar ke layar sehingga mungkin tidak persis seperti yang Anda cari. Saya hanya melihat melalui semua metode UITableViewdan UIScrollViewdelegasi dan tampaknya tidak ada yang harus ditangani tepat setelah sel dimasukkan animasi.


Mengapa tidak memanggil metode Anda ingin dipanggil ketika animasi berakhir setelah endUpdates?

- (void)setDownloadedImage:(NSMutableDictionary *)d {
    NSIndexPath *indexPath = (NSIndexPath *)[d objectForKey:@"IndexPath"];
    [indexPathDelayed addObject:indexPath];
    if (!([table isDragging] || [table isDecelerating])) {
        [table beginUpdates];
        [table insertRowsAtIndexPaths:indexPathDelayed withRowAnimation:UITableViewRowAnimationFade];
        [table endUpdates];
        // --> Call Method Here <--
        loadingView.hidden = YES;
        [indexPathDelayed removeAllObjects];
    }
}
chown
sumber
Terima kasih, chown. Jangan berpikir willDisplayCellakan berhasil. Saya membutuhkannya terjadi setelah animasi karena saya perlu melakukan pembaruan visual hanya setelah semuanya beres.
pixelfreak
np @pixelfreak, jika saya memikirkan cara lain untuk melakukan ini, saya akan memberi tahu Anda.
chown