Memuat UITableViewCell yang Dapat Digunakan Kembali dari Nib

89

Saya bisa merancang UITableViewCells kustom dan memuatnya dengan baik menggunakan teknik yang dijelaskan dalam utas yang ditemukan di http://forums.macrumors.com/showthread.php?t=545061 . Namun, menggunakan metode itu tidak lagi memungkinkan Anda untuk menginisialisasi sel dengan reuseIdentifier yang berarti Anda harus membuat instance baru dari setiap sel di setiap panggilan. Adakah yang menemukan cara yang baik untuk tetap menyimpan jenis sel tertentu untuk digunakan kembali, tetapi masih dapat mendesainnya di Interface Builder?

Greg Martin
sumber

Jawaban:

74

Cukup terapkan metode dengan tanda tangan metode yang sesuai:

- (NSString *) reuseIdentifier {
  return @"myIdentifier";
}
Louis Gerbarg
sumber
Di mana menerapkan metode ini?
Krishnan
2
Di subkelas UITableViewCell Anda. developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/…
DenTheMan
5
Ini beresiko. Apa yang terjadi jika Anda memiliki dua subclass dari subclass sel Anda, dan menggunakan keduanya dalam satu tampilan tabel? Jika mereka mengirim panggilan pengenal reuse ke super, Anda akan membatalkan antrean sel dengan jenis yang salah ............. Saya rasa Anda perlu mengganti metode reuseIdentifier, tetapi mengembalikan pengenal yang diganti tali.
SK9
3
Untuk memastikannya unik, Anda dapat melakukan:return NSStringFromClass([self class]);
ivanzoid
119

Sebenarnya, karena Anda sedang membangun sel di Interface Builder, cukup setel pengenal penggunaan kembali di sana:

IB_reuse_identifier

Atau jika Anda menjalankan Xcode 4, periksa tab Attributes inspector:

masukkan deskripsi gambar di sini

(Sunting: Setelah XIB Anda dibuat oleh XCode, itu berisi UIView kosong, tapi kami membutuhkan UITableViewCell; jadi Anda harus menghapus UIView secara manual dan memasukkan Tabel Tampilan Sel. Tentu saja, IB tidak akan menampilkan parameter UITableViewCell apa pun untuk UIView.)

Tim Keating
sumber
Apa yang harus saya atur Pengidentifikasi, jika saya menggunakan Nib yang dibuat Sel untuk lebih dari satu sel? Ini akan membuat kita memiliki dua sel dengan pengenal yang sama.
Krishnan
4
Jangan menganggapnya sebagai pengenal unik - anggap saja lebih seperti nama tipe.
Tim Keating
Saya tidak melihat opsi untuk menyetel pengenal melalui pembuat antarmuka bawaan di Xcode 4.3.3. Saya pasti mengatur kelas ke subkelas UITableViewCell saya. Apakah saya hanya melewatkannya, atau hilang?
Tyler
3
Oke, saya sudah pecahkan masalah saya. Jika Anda memulai dengan objek UIView di pembuat antarmuka (dalam Xcode 4), dan mengubah kelasnya menjadi UITableViewCell, Anda tidak mendapatkan properti khusus sel seperti pengenal penggunaan kembali. Untuk mendapatkannya, Anda harus memulai dengan xib kosong dan menyeret objek sel tabel, yang kemudian akan memiliki properti khusus sel yang dapat Anda edit.
Tyler
1
@Krishnan Pikirkan seperti ini - saat Anda membuat sel tampilan tabel dengan pengenal X, Anda mengatakan "Beri saya sel dari kumpulan berlabel X." Jika kolam itu ada, dan ada sel gratis di sana, maka itu memberikannya kepada Anda. Jika tidak, ia akan membuat kumpulan (jika perlu), lalu menaikkan sel, memberinya label "X", dan kemudian menyerahkannya kepada Anda. Jadi sel DAPAT unik - misalnya Anda dapat membuat kumpulan dengan hanya satu sel dengan pengenal tertentu - tetapi pustaka menggunakan strategi seperti daftar gratis untuk menghindari alokasi / dealloc memori.
Tim Keating
66

Sekarang, di iOS 5 ada metode UITableView yang sesuai untuk itu:

- (void)registerNib:(UINib *)nib forCellReuseIdentifier:(NSString *)identifier
wzs
sumber
10
Balasan lain di utas ini, termasuk jawaban yang diterima, berisi saran usang.
Kaelin Colclasure
apakah ini kompatibel dengan versi sebelumnya? Maksud saya, jika saya mengembangkan aplikasi dengan SDK 5.0 dan menargetkan minimum 4.0, apakah aplikasi akan berjalan di perangkat dengan iOS 4.0 misalnya?
Abolfoooud
1
Tidak, ini tidak kompatibel mundur, seperti API baru di iOS 5.0.
marzapower
contoh yang berhasil tentang cara mengintegrasikan ini ke pengontrol Anda: mindfiresolutions.com/…
mblackwell8
47

Saya tidak ingat di mana awalnya saya menemukan kode ini, tetapi sejauh ini berhasil dengan baik untuk saya.

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

    static NSString *CellIdentifier = @"CustomTableCell";
    static NSString *CellNib = @"CustomTableCellView";

    UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        NSArray *nib = [[NSBundle mainBundle] loadNibNamed:CellNib owner:self options:nil];
        cell = (UITableViewCell *)[nib objectAtIndex:0];
    }

    // perform additional custom work...

    return cell;
}

Contoh penyiapan Pembuat Antarmuka ...

teks alt

busur panah
sumber
12

Lihatlah jawaban yang saya berikan untuk pertanyaan ini:

Apakah mungkin untuk mendesain subclass NSCell di Interface Builder?

Tidak hanya mungkin mendesain UITableViewCell dalam IB, tetapi juga diinginkan karena jika tidak, semua kabel manual dan penempatan beberapa elemen sangat membosankan. Performaance baik-baik saja selama Anda berhati-hati untuk membuat semua elemen menjadi buram jika memungkinkan. ReuseID disetel di IB untuk properti UITableViewCell, lalu Anda menggunakan ID reuse yang cocok dalam kode saat mencoba melakukan dequeue.

Saya juga mendengar dari beberapa presenter di WWDC tahun lalu bahwa Anda tidak boleh membuat sel tampilan tabel di IB, tetapi ini adalah beban omong kosong.

Kendall Helmstetter Gelner
sumber
2
Anda tidak boleh membuat sel tampilan tabel di IB jika Anda membutuhkan transparansi dan ingin kinerja pengguliran yang baik. Untuk UI tertentu, Anda memerlukan transparansi (misalnya untuk merender teks di atas grafik). Terkadang satu-satunya cara untuk mencapai pengguliran yang baik pada perangkat keras yang lebih lama (pra-A4) adalah dengan membuat kode, untuk menghindari GPU harus menggabungkan beberapa lapisan transparan.
Nick Forge
2
Benar, tetapi bahkan dengan itu Anda mungkin lebih baik membiarkan kinerja sedikit menurun pada perangkat lama untuk memudahkan pemeliharaan sel yang dibangun di IB. Anda juga dapat menggunakan teknik ini sebagai sel template yang kemudian Anda gambar elemennya untuk menghindari penggabungan dengan metode gambar kustom.
Kendall Helmstetter Gelner
6

Ini opsi lain:

NSString * cellId = @"reuseCell";  
//...
NSArray * nibObjects = [[NSBundle mainBundle] loadNibNamed:@"CustomTableCell" owner:nil options:nil];

for (id obj in nibObjects)
{
    if ([obj isKindOfClass:[CustomTableCell class]])
    {
        cell = obj;
        [cell setValue:cellId forKey:@"reuseIdentifier"];
        break;
    }
}
JDL
sumber
Perhatikan bahwa ini adalah satu-satunya solusi yang diposting sejauh ini yang tidak memerlukan subclass kustom Anda UITableViewCelluntuk menetapkan nilai unik untuk reuseIdentifer. Saya pikir inilah yang sebenarnya dicari oleh op asli.
charshep
Saya berharap Apple tidak akan menolak aplikasi saya untuk menggunakan ini ... Saya menggunakan ini untuk mendapatkan sel "statis" karena dalam tampilan tabel saya, proses pengisian sel agak lambat. Dengan cara ini saya hanya perlu menjalankannya sekali (memberikan pengenal yang berbeda untuk setiap baris). Terima kasih!
Ricard Pérez del Campo
Sangat membantu karena tidak ada metode UITableViewCell untuk menyetel ini secara manual!
Jesse
2

Saya membuat sel tampilan kustom saya dengan cara yang sama - kecuali saya menghubungkan sel melalui IBOutlet.

The [nib objectAt...]pendekatan adalah rentan terhadap perubahan posisi dari item dalam array.

The UIViewControllerpendekatan baik - hanya mencoba, dan bekerja cukup baik.

TAPI...

Dalam semua kasus, initWithStylekonstruktor TIDAK dipanggil, jadi tidak ada inisialisasi default yang dilakukan.

Saya telah membaca berbagai tempat tentang penggunaan initWithCoderatau awakeFromNib, tetapi tidak ada bukti konklusif bahwa salah satu dari ini adalah cara yang benar.

Selain secara eksplisit memanggil beberapa metode inisialisasi dalam cellForRowAtIndexPathmetode ini, saya belum menemukan jawaban untuk ini.

sww
sumber
awakeFromNib adalah cara yang tepat untuk bereaksi terhadap objek yang sedang dimuat dari NIB.
Jon Hess
2

Beberapa waktu yang lalu saya menemukan posting blog yang bagus tentang topik ini di blog.atebits.com , dan sejak itu mulai menggunakan kelas Loren Brichter ABTableViewCell untuk melakukan semua UITableViewCells saya.

Anda berakhir dengan UIView wadah sederhana untuk meletakkan semua widget Anda, dan pengguliran secepat kilat.

Semoga bermanfaat.

eric
sumber
2

Teknik ini juga berfungsi dan tidak memerlukan ivar yang funky di pengontrol tampilan Anda untuk manajemen memori. Di sini, sel tampilan tabel kustom berada dalam xib bernama "CustomCell.xib".

 static NSData *sLoadedCustomCell = nil;

 cell = [tableView dequeueReusableCellWithIdentifier:@"CustomCell"];
 if (cell == nil) 
 {
   if (sLoadedCustomCell == nil) 
   {        
      // Load the custom table cell xib
      // and extract a reference to the cell object returned
      // and cache it in a static to avoid reloading the nib again.

      for (id loadedObject in [[NSBundle mainBundle] loadNibNamed:@"CustomCell" owner:nil options:nil]) 
      {
        if ([loadedObject isKindOfClass:[UITableViewCell class]]) 
        {
          sLoadedCustomCell = [[NSKeyedArchiver archivedDataWithRootObject: loadedObject] retain];
          break;
        }
    }
    cell = (UITableViewCell *)[NSKeyedUnarchiver unarchiveObjectWithData: sLoadedCustomCell];
  }
Bill Garrison
sumber
1
Mengarsipkan dan membatalkan pengarsipan sama sekali tidak perlu.
Bryan Henry
1
Pengarsipan / pengarsipan tidak diperlukan jika Anda tidak keberatan memuat sel dari ujungnya setiap kali sel tidak dapat di-dequeued. Namun, jika Anda hanya ingin memuat sel dari ujungnya tepat satu kali , Anda perlu menyimpannya dalam cache di memori. Saya menyelesaikan caching itu menggunakan NSKeyedArchiving karena UITableViewCell tidak mengimplementasikan NSCopying.
Bill Garrison
1
Semua yang dikatakan, menggunakan UINib untuk memuat sel mencapai efek yang sama: memuat dari disk sekali, memuat dari memori sesudahnya.
Bill Garrison
2

Metode Louis berhasil untuk saya. Ini adalah kode yang saya gunakan untuk membuat UITableViewCell dari ujung pena:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{   
    UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:@"CustomCellId"];

    if (cell == nil) 
    {
        UIViewController *c = [[UIViewController alloc] initWithNibName:@"CustomCell" bundle:nil];
        cell = (PostCell *)c.view;
        [c release];
    }

    return cell;
}
ggarber
sumber
2
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

static NSString *simpleTableIdentifier = @"CustomCell";

CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil)
{
    NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"CustomCell" owner:self options:nil];
    cell = [nib objectAtIndex:0];

    [cell setSelectionStyle:UITableViewCellSelectionStyleNone];
}         

return cell;
}
Mohit
sumber
1

Solusi gustavogb tidak berhasil untuk saya, yang saya coba adalah:

ChainesController *c = [[ChainesController alloc] initWithNibName:@"ChainesController" bundle:nil];
[[NSBundle mainBundle] loadNibNamed:@"ChaineArticleCell" owner:c options:nil];
cell = [c.blogTableViewCell retain];
[c release];

Sepertinya berhasil. BlogTableViewCell adalah IBOutlet untuk sel dan ChainesController adalah pemilik file.

groumpf
sumber
1

Dari dokumen UITableView tentang dequeueWithReuseIdentifier: "Sebuah string yang mengidentifikasi objek sel untuk digunakan kembali. Secara default, pengidentifikasi sel yang dapat digunakan kembali adalah nama kelasnya, tetapi Anda dapat mengubahnya menjadi nilai arbitrer."

Overriding -reuseIdentifer sendiri berisiko. Apa yang terjadi jika Anda memiliki dua subclass dari subclass sel Anda, dan menggunakan keduanya dalam satu tampilan tabel? Jika mereka mengirim panggilan pengenal reuse ke super, Anda akan membatalkan antrean sel dengan jenis yang salah ............. Saya rasa Anda perlu mengganti metode reuseIdentifier, tetapi mengembalikan pengenal yang diganti tali. Atau, jika salah satu belum ditentukan, kembalikan kelas sebagai string.

SK9
sumber
0

Untuk apa nilainya, saya bertanya kepada insinyur iPhone tentang ini di salah satu Pembicaraan Teknologi iPhone. Jawabannya adalah, "Ya, IB dapat digunakan untuk membuat sel. Tapi jangan. Tolong, jangan."

Agustus
sumber
1
Itu aneh. Setidaknya dua pembicaraan di NY talk memiliki kode demo yang menggunakan sel yang dibuat di IB.
Shawn Craver
3
Tidak yakin saya percaya ini karena mereka menggunakan IB untuk membuat sel di proyek contoh Sel Tampilan Tabel Tingkat Lanjut Apple.
dirampok
Terima kasih untuk itu. Setiap kali saya melakukannya, saya mengalami masalah. Ini bisa jadi alasannya
skorulis
Dia mungkin tidak memiliki cukup pengetahuan tentang itu.
aryaxt
0

Saya mengikuti instruksi Apple yang ditautkan oleh Ben Mosher (terima kasih!) Tetapi menemukan bahwa Apple menghilangkan poin penting. Objek yang mereka desain di IB hanyalah UITableViewCell, seperti variabel yang mereka muat darinya. Tetapi jika Anda benar-benar menyiapkannya sebagai subkelas khusus dari UITableViewCell, dan menulis file kode untuk subkelas tersebut, Anda dapat menulis deklarasi IBOutlet dan metode IBAction dalam kode dan menyambungkannya ke elemen khusus Anda di IB. Maka tidak perlu menggunakan tag tampilan untuk mengakses elemen ini dan Anda dapat membuat sel gila apa pun yang Anda inginkan. Ini surga Cocoa Touch.

David Casseres
sumber