Miliki reloadData untuk UITableView yang hidup saat berubah

183

Saya memiliki UITableView yang memiliki dua mode. Ketika kami beralih di antara mode, saya memiliki jumlah bagian dan sel yang berbeda per bagian. Idealnya, itu akan melakukan beberapa animasi keren ketika meja tumbuh atau menyusut.

Ini kode yang saya coba, tetapi tidak melakukan apa-apa:

CGContextRef context = UIGraphicsGetCurrentContext(); 
[UIView beginAnimations:nil context:context]; 
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut]; 
[UIView setAnimationDuration:0.5]; 

[self.tableView reloadData];
[UIView commitAnimations];

Adakah pemikiran tentang bagaimana saya bisa melakukan ini?

Vertexwahn
sumber

Jawaban:

400

Sebenarnya, ini sangat sederhana:

[_tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationFade];

Dari dokumentasi :

Memanggil metode ini menyebabkan tampilan tabel untuk meminta sumber data untuk sel baru untuk bagian yang ditentukan. Tampilan tabel menjiwai penyisipan sel-sel baru di saat ia menghidupkan sel-sel lama keluar.

dmarnel
sumber
13
Dan hanya jawaban terbaik di halaman ini!
Matthias D
41
Ini tidak sepenuhnya benar, karena hanya akan memuat ulang bagian pertama. Jika tabel Anda memiliki beberapa bagian, ini tidak akan berfungsi
Nosrettap
4
Ada kemungkinan Anda akan mendapatkan pengecualian jika tidak ada data yang berubah .. Lihat jawaban
Matej
8
@Nosrettap: jika Anda ingin memiliki lebih banyak atau semua bagian dari tabel Anda memuat ulang, yang harus Anda lakukan adalah memperpanjang NSIndexSet Anda dengan semua indeks bagian, yang perlu di-refresh juga
JonEasy
Versi cepat: _tableView.reloadSections(NSIndexSet(index: 0), withRowAnimation: .Fade)Khususnya, itu hanya memperbarui Bagian 0, yang merupakan bagian pertama default.
kbpontius
264

Anda mungkin ingin menggunakan:

Objektif-C

[UIView transitionWithView: self.tableView
                  duration: 0.35f
                   options: UIViewAnimationOptionTransitionCrossDissolve
                animations: ^(void)
 {
      [self.tableView reloadData];
 }
                completion: nil];

Cepat

UIView.transitionWithView(tableView,
                          duration: 0.35,
                          options: .TransitionCrossDissolve,
                          animations:
{ () -> Void in
    self.tableView.reloadData()
},
                          completion: nil);

Swift 3, 4 & 5

UIView.transition(with: tableView,
                  duration: 0.35,
                  options: .transitionCrossDissolve,
                  animations: { self.tableView.reloadData() }) // left out the unnecessary syntax in the completion block and the optional completion parameter

Tidak ada kerepotan. : D

Anda juga dapat menggunakan salah satu yang UIViewAnimationOptionTransitionsAnda inginkan untuk efek dingin:

  • transitionNone
  • transitionFlipFromLeft
  • transitionFlipFromRight
  • transitionCurlUp
  • transitionCurlDown
  • transitionCrossDissolve
  • transitionFlipFromTop
  • transitionFlipFromBottom
Kenn Cal
sumber
2
Ini berguna jika keadaan awal / akhir dari tampilan tabel Anda akan sangat berbeda (dan akan rumit untuk menghitung bagian dan baris untuk menambah / menghapus), tetapi Anda memiliki sesuatu yang kurang menggembirakan bahwa reload non-animasi.
Ben Packard
1
Animasi ini tidak sebagus memuat ulang tampilan tabel menggunakan metode yang dibangun dalam metode ini, tetapi tidak seperti metode berperingkat lebih tinggi yang disebutkan di sini, animasi ini berfungsi ketika Anda memiliki beberapa bagian.
Mark Bridges
1
Wow setelah mencoba semua ini, ini sangat cocok untuk saya
Lucas Goossen
1
@MarkBridges jawaban yang berperingkat lebih tinggi tidak bekerja dengan beberapa bagian :) -[_tableView reloadSections:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, sectionCount)] withRowAnimation:UITableViewRowAnimationFade];
lobianco
2
@Anthony Tidak berfungsi jika kondisi akhir memiliki lebih banyak / lebih sedikit bagian daripada kondisi awal. Maka Anda harus secara manual melacak bagian mana yang ditambahkan / dihapus, yang cukup merepotkan.
nikolovski
78

Memiliki lebih banyak kebebasan menggunakan CATransitionkelas.

Itu tidak terbatas pada memudar, tetapi dapat melakukan gerakan juga ..


Sebagai contoh:

(jangan lupa impor QuartzCore)

CATransition *transition = [CATransition animation];
transition.type = kCATransitionPush;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.fillMode = kCAFillModeForwards;
transition.duration = 0.5;
transition.subtype = kCATransitionFromBottom;

[[self.tableView layer] addAnimation:transition forKey:@"UITableViewReloadDataAnimationKey"];

Ubah typeagar sesuai dengan kebutuhan Anda, seperti kCATransitionFadedll.

Implementasi di Swift:

let transition = CATransition()
transition.type = kCATransitionPush
transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
transition.fillMode = kCAFillModeForwards
transition.duration = 0.5
transition.subtype = kCATransitionFromTop
self.tableView.layer.addAnimation(transition, forKey: "UITableViewReloadDataAnimationKey")
// Update your data source here
self.tableView.reloadData()

Referensi untuk transisi CAT

Swift 5:

let transition = CATransition()
transition.type = CATransitionType.push
transition.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
transition.fillMode = CAMediaTimingFillMode.forwards
transition.duration = 0.5
transition.subtype = CATransitionSubtype.fromTop
self.tableView.layer.add(transition, forKey: "UITableViewReloadDataAnimationKey")
// Update your data source here
self.tableView.reloadData()
Matej
sumber
ini menjiwai tabel secara keseluruhan, bukan baris tunggal / bagian.
Agos
@ Agos Masih menjawab pertanyaan. Sebuah pertanyaan "Bagaimana cara memuat ulang hanya satu baris" memiliki jawaban yang sangat berbeda, seperti menerapkan animasi ke layerproperti a UITableViewCell.
Matej
@matejkramny Saya mengharapkan tabel untuk menghidupkan hanya baris yang berbeda (seperti yang dirujuk dalam pertanyaan), tetapi metode ini mendorong semua tabel sekaligus. Mungkin saya kehilangan sesuatu?
Agos
@ Agos hmm tidak seharusnya melakukan itu. Ini tidak dapat dilakukan hanya setengah tabel karena QuartzCore memodifikasi tampilan secara langsung. Anda dapat mencoba mendapatkan sel dari tampilan tabel, dan kemudian menerapkan animasi ini untuk masing-masing (tapi tidak yakin itu akan berhasil)
Matej
2
bekerja seperti pesona dengan kCATransitionFade Terima kasih! :)
quarezz
60

Saya yakin Anda bisa memperbarui struktur data Anda, lalu:

[tableView beginUpdates];
[tableView deleteSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:YES];
[tableView insertSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:YES];
[tableView endUpdates];

Juga, "withRowAnimation" bukan boolean, tetapi gaya animasi:

UITableViewRowAnimationFade,
UITableViewRowAnimationRight,
UITableViewRowAnimationLeft,
UITableViewRowAnimationTop,
UITableViewRowAnimationBottom,
UITableViewRowAnimationNone,
UITableViewRowAnimationMiddle
Tiago Fael Matos
sumber
Hal-hal sederhana, tetapi sangat mudah untuk langsung menuju StackOverflow dan mendapatkan potongan yang Anda butuhkan, daripada pukat dokumen. . Terima kasih!
Jasper Blues
24

Semua jawaban ini mengasumsikan bahwa Anda menggunakan UITableView dengan hanya 1 bagian.

Untuk secara akurat menangani situasi di mana Anda memiliki lebih dari 1 bagian, gunakan:

NSRange range = NSMakeRange(0, myTableView.numberOfSections);
NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:range];
[myTableView reloadSections:indexSet withRowAnimation:UITableViewRowAnimationAutomatic];

(Catatan: Anda harus memastikan bahwa Anda memiliki lebih dari 0 bagian!)

Hal lain yang perlu diperhatikan adalah bahwa Anda mungkin mengalami NSInternalInconsistencyException jika Anda mencoba memperbarui sumber data Anda secara bersamaan dengan kode ini. Jika demikian, Anda dapat menggunakan logika yang serupa dengan ini:

int sectionNumber = 0; //Note that your section may be different

int nextIndex = [currentItems count]; //starting index of newly added items

[myTableView beginUpdates];

for (NSObject *item in itemsToAdd) {
    //Add the item to the data source
    [currentItems addObject:item];

    //Add the item to the table view
    NSIndexPath *path = [NSIndexPath indexPathForRow:nextIndex++ inSection:sectionNumber];
    [myTableView insertRowsAtIndexPaths:[NSArray arrayWithObject:path] withRowAnimation:UITableViewRowAnimationAutomatic];
}

[myTableView endUpdates];
Diadyne
sumber
3
Poin bagus tentang perhitungan bagian, tetapi saya hanya memiliki satu bagian dan kode Anda memiliki kesalahan tidak langsung. Saya harus mengubah baris pertama kode Anda ke rentang NSRange = NSMakeRange (0, myTableView.numberOfSections);
Danyal Aytekin
18

Cara untuk mendekati ini adalah dengan memberitahu tableView untuk menghapus dan menambahkan baris dan bagian dengan

insertRowsAtIndexPaths:withRowAnimation:,
deleteRowsAtIndexPaths:withRowAnimation:,
insertSections:withRowAnimation:Dan
deleteSections:withRowAnimation:

metode UITableView.

Saat Anda memanggil metode ini, tabel akan menggerakkan masuk / keluar item yang Anda minta, lalu memanggil reloadData dengan sendirinya sehingga Anda dapat memperbarui status setelah animasi ini. Bagian ini penting - jika Anda menghidupkan segala sesuatu tetapi tidak mengubah data yang dikembalikan oleh dataSource tabel, baris akan muncul lagi setelah animasi selesai.

Jadi, alur aplikasi Anda adalah:

[self setTableIsInSecondState:YES];

[myTable deleteSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:YES]];

Selama metode dataSource tabel Anda mengembalikan set bagian dan baris baru yang benar dengan memeriksa [self tableIsInSecondState](atau apa pun), ini akan mencapai efek yang Anda cari.

iKenndac
sumber
16

Saya tidak bisa mengomentari jawaban teratas, tetapi implementasi cepat adalah:

self.tableView.reloadSections([0], with: UITableViewRowAnimation.fade)

Anda dapat memasukkan sebanyak mungkin bagian yang ingin Anda perbarui dalam argumen pertama untuk reloadSections.

Animasi lain yang tersedia dari dokumen: https://developer.apple.com/reference/uikit/uitableviewrowanimation

fade Baris atau baris yang dimasukkan atau dihapus memudar ke dalam atau keluar dari tampilan tabel.

kanan Baris atau baris yang dimasukkan meluncur dari kanan; baris atau baris yang dihapus meluncur ke kanan.

kiri Baris atau baris yang dimasukkan meluncur dari kiri; baris atau baris yang dihapus geser ke kiri.

atas Baris atau baris yang dimasukkan meluncur dari atas; baris atau baris yang dihapus meluncur keluar ke atas.

bawah Baris atau baris yang dimasukkan meluncur dari bawah; baris atau baris yang dihapus meluncur keluar ke bawah.

huruf besar-kecil Baris yang dimasukkan atau dihapus menggunakan animasi default.

tengah tampilan tabel mencoba untuk menjaga sel-sel lama dan baru berpusat di ruang yang mereka tempati atau akan tempati. Tersedia di iPhone 3.2.

otomatis Tampilan tabel memilih gaya animasi yang sesuai untuk Anda. (Diperkenalkan di iOS 5.0.)

Christopher Larsen
sumber
1
Untuk Swift 4.2: self.tableView.reloadSections ([0], dengan: UITableView.RowAnimation.fade)
Reefwing
14

Versi Swift 4 untuk jawaban @dmarnel:

tableView.reloadSections(IndexSet(integer: 0), with: .automatic)
chengsam
sumber
9

Implementasi Cepat:

let range = NSMakeRange(0, self.tableView!.numberOfSections())
let indexSet = NSIndexSet(indexesInRange: range)
self.tableView!.reloadSections(indexSet, withRowAnimation: UITableViewRowAnimation.Automatic)
Michael Peterson
sumber
8

Untuk Swift 4

tableView.reloadSections([0], with: UITableView.RowAnimation.fade)
Claus
sumber
1

Dalam kasus saya, saya ingin menambahkan 10 baris lagi ke tampilan tabel (untuk jenis fungsi "tampilkan lebih banyak hasil") dan saya melakukan yang berikut:

  NSInteger tempNumber = self.numberOfRows;
  self.numberOfRows += 10;
  NSMutableArray *arrayOfIndexPaths = [[NSMutableArray alloc] init];
  for (NSInteger i = tempNumber; i < self.numberOfRows; i++) {
    [arrayOfIndexPaths addObject:[NSIndexPath indexPathForRow:i inSection:0]];
  }
  [self.tableView beginUpdates];
  [self.tableView insertRowsAtIndexPaths:arrayOfIndexPaths withRowAnimation:UITableViewRowAnimationTop];
  [self.tableView endUpdates];

Dalam kebanyakan kasus, alih-alih "self.numberOfRows", Anda biasanya akan menggunakan jumlah array objek untuk tampilan tabel. Jadi untuk memastikan solusi ini bekerja dengan baik untuk Anda, "arrayOfIndexPaths" perlu array yang akurat dari jalur indeks dari baris yang dimasukkan. Jika baris ada untuk salah satu jalur indeks ini, kode mungkin macet, jadi Anda harus menggunakan metode "reloadRowsAtIndexPaths: withRowAnimation:" untuk indeks pathds tersebut untuk menghindari tabrakan

Lucas
sumber
1

Jika Anda ingin menambahkan animasi kustom Anda sendiri ke sel UITableView, gunakan

[theTableView reloadData];
[theTableView layoutSubviews];
NSArray* visibleViews = [theTableView visibleCells];

untuk mendapatkan array sel yang terlihat. Kemudian tambahkan animasi khusus ke setiap sel.

Lihat intisari yang saya posting untuk animasi sel kustom yang halus. https://gist.github.com/floprr/1b7a58e4a18449d962bd

flopr
sumber
1

Animasi tanpa reloadData () di Swift dapat dilakukan seperti ini (pada versi 2.2):

tableview.beginUpdates()
var indexPathsToDeleteForAnimation: [NSIndexPath] = []
var numOfCellsToRemove = ArrayOfItemsToRemove ?? 0

// Do your work here
while numOfCellsToRemove > 0 {
    // ...or here, if you need to add/remove the same amount of objects to/from somewhere
    indexPathsToDeleteForAnimation.append(NSIndexPath(forRow: selectedCellIndex+numOfCellsToRemove, inSection: 0))
    numOfCellsToRemove -= 1
}
tableview.deleteRowsAtIndexPaths(indexPathsToDeleteForAnimation, withRowAnimation: UITableViewRowAnimation.Right)
tableview.endUpdates()

jika Anda perlu memanggil reloadData () setelah animasi berakhir, Anda dapat merangkul perubahan dalam transaksi CAT seperti ini:

CATransaction.begin()
CATransaction.setCompletionBlock({() in self.tableview.reloadData() })
tableview.beginUpdates()
var indexPathsToDeleteForAnimation: [NSIndexPath] = []
var numOfCellsToRemove = ArrayOfItemsToRemove.count ?? 0

// Do your work here
while numOfCellsToRemove > 0 {
     // ...or here, if you need to add/remove the same amount of objects to/from somewhere
     indexPathsToDeleteForAnimation.append(NSIndexPath(forRow: selectedCellIndex+numOfCellsToRemove, inSection: 0))
     numOfCellsToRemove -= 1
}
tableview.deleteRowsAtIndexPaths(indexPathsToDeleteForAnimation, withRowAnimation: UITableViewRowAnimation.Right)
tableview.endUpdates()
CATransaction.commit()

Logika ditampilkan untuk kasus ketika Anda menghapus baris, tetapi ide yang sama juga berfungsi untuk menambahkan baris. Anda juga dapat mengubah animasi menjadi UITableViewRowAnimation.Left untuk membuatnya rapi, atau pilih dari daftar animasi lain yang tersedia.

Vitalii
sumber
1
CATransition *animation = [CATransition animation];
animation.duration = .3;
[animation setType:kCATransitionPush];
[animation setSubtype:kCATransitionFromLeft];
[animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
[animation setDuration:.3];

[[_elementTableView layer] addAnimation:animation forKey:@"UITableViewReloadDataAnimationKey"];

[tableView reloadData];
Mayur Sardana
sumber
2
Mengapa mengatur durasi .3dua kali?
Pang
1

Untuk memuat ulang semua bagian , bukan hanya satu dengan durasi kustom .

durationParameter pengguna UIView.animateuntuk mengatur durasi kustom.

UIView.animate(withDuration: 0.4, animations: { [weak self] in
    guard let `self` = self else { return }
    let indexSet = IndexSet(integersIn: 0..<self.tableView.numberOfSections)
    self.tableView.reloadSections(indexSet, with: UITableView.RowAnimation.fade)
})
JPetric
sumber
0

UITableViewAnimasi asli di Swift

Sisipkan dan hapus baris sekaligus dengan tableView.performBatchUpdatesbegitu mereka terjadi secara bersamaan. Membangun jawaban dari @iKenndac menggunakan metode seperti:

  • tableView.insertSections
  • tableView.insertRows
  • tableView.deleteSections
  • tableView.deleteRows

Ex:

 tableView.performBatchUpdates({
   tableView.insertSections([0], with: .top)
 })

Ini menyisipkan bagian di posisi nol dengan animasi yang dimuat dari atas. Ini akan menjalankan kembali cellForRowAtmetode dan memeriksa sel baru di posisi itu. Anda bisa memuat ulang seluruh tampilan tabel dengan animasi tertentu.

Re: pertanyaan OP, bendera bersyarat akan diperlukan untuk menunjukkan sel untuk keadaan tampilan tabel alternatif.

kerajinan
sumber