Bagaimana Anda memuat UITableViewCells khusus dari file Xib?


293

Pertanyaannya sederhana: Bagaimana Anda memuat custom UITableViewCelldari file Xib? Melakukannya memungkinkan Anda menggunakan Interface Builder untuk mendesain sel Anda. Jawabannya ternyata tidak sederhana karena masalah manajemen memori. Utas ini menyebutkan masalah dan menyarankan solusi, tetapi merupakan pra-rilis NDA dan tidak memiliki kode. Inilah utas panjang yang membahas masalah ini tanpa memberikan jawaban yang pasti.

Berikut beberapa kode yang saya gunakan:

static NSString *CellIdentifier = @"MyCellIdentifier";

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

Untuk menggunakan kode ini, buat MyCell.m / .h, subclass baru UITableViewCelldan tambahkan IBOutletsuntuk komponen yang Anda inginkan. Kemudian buat file "Empty XIB" baru. Buka file Xib di IB, tambahkan UITableViewCellobjek, atur pengenalnya ke "MyCellIdentifier", dan atur kelasnya ke MyCell dan tambahkan komponen Anda. Akhirnya, hubungkan IBOutletske komponen. Perhatikan bahwa kami tidak menetapkan Pemilik File di IB.

Metode lain menganjurkan pengaturan Pemilik File dan memperingatkan kebocoran memori jika Xib tidak dimuat melalui kelas pabrik tambahan. Saya menguji di atas di bawah Instrumen / Kebocoran dan tidak melihat kebocoran memori.

Jadi apa cara kanonik untuk memuat sel dari Xibs? Apakah kita menetapkan Pemilik File? Apakah kita memerlukan pabrik? Jika demikian, seperti apa kode untuk pabrik itu? Jika ada beberapa solusi, mari kita perjelas pro dan kontra dari masing-masing ...


2
Dapatkah seseorang mengedit subjek untuk benar-benar mengajukan pertanyaan, yaitu "Bagaimana Anda memuat UITableViewCells khusus dari file Xib?" (Abaikan jika ini tidak mungkin pada stackoverflow.)
Steven Fisher

1
Untuk iOS 5 dan seterusnya, ini solusinya: stackoverflow.com/questions/15591364/… , yang sama dengan solusi giuseppe.
Matt Becker

Catatan cepat, lebih sederhana (milieu 2013) jawab di sini stackoverflow.com/questions/15378788/... jamihash
Fattie

Jawaban:


288

Berikut adalah dua metode yang menyatakan penulis asli direkomendasikan oleh seorang insinyur IB .

Lihat posting aktual untuk lebih jelasnya. Saya lebih suka metode # 2 karena tampaknya lebih sederhana.

Metode # 1:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"BDCustomCell"];
    if (cell == nil) {
        // Create a temporary UIViewController to instantiate the custom cell.
        UIViewController *temporaryController = [[UIViewController alloc] initWithNibName:@"BDCustomCell" bundle:nil];
        // Grab a pointer to the custom cell.
        cell = (BDCustomCell *)temporaryController.view;
        [[cell retain] autorelease];
        // Release the temporary UIViewController.
        [temporaryController release];
    }

    return cell;
}

Metode # 2:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"BDCustomCell"];
    if (cell == nil) {
        // Load the top-level objects from the custom cell XIB.
        NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"BDCustomCell" owner:self options:nil];
        // Grab a pointer to the first object (presumably the custom cell, as that's all the XIB should contain).
        cell = [topLevelObjects objectAtIndex:0];
    }

    return cell;
}

Pembaruan (2014): Metode # 2 masih valid tetapi tidak ada dokumentasi untuk itu lagi. Dulu berada di dokumen resmi tetapi sekarang dihapus demi storyboard.

Saya memposting contoh yang berfungsi di Github:
https://github.com/bentford/NibTableCellExample

edit untuk Swift 4.2

override func viewDidLoad() {
    super.viewDidLoad()

    // Do any additional setup after loading the view.
    self.tblContacts.register(UINib(nibName: CellNames.ContactsCell, bundle: nil), forCellReuseIdentifier: MyIdentifier)
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    let cell = tableView.dequeueReusableCell(withIdentifier: MyIdentifier, for: indexPath) as! ContactsCell

    return cell
}

1
Untuk metode 1, sebaiknya Anda tidak melakukan sesuatu seperti "cell = (BDCustomCell *) [[temporaryController.view retain] autorelease];" jadi sel tidak bisa dirilis ketika pengontrol sementara dilepaskan?
Tod Cunningham

Hm Dokumentasi yang berbicara tentang # 2 masih memberitahu Anda untuk mengatur pemilik sel dalam file XIB, ke kelas controller yang dikenal. Mungkin tidak masalah ketika Anda mengatur pemilik saat memuat.
Oscar

@OscarGoldman Pemilik sel dalam file XIB adalah kelas (mis. Tipe pemilik.) Pemilik sel di loadNibNamed: owner: options: adalah objek dari tipe yang ditentukan dalam XIB.
bentford

2
Opsi @CoolDocMan # 2 masih berfungsi. Masalah kemungkinan besar dengan ujung. Berikut ini sebuah contoh: github.com/bentford/NibTableCellExample
bentford

2
Kenapa kode super tua ini berperingkat sangat tinggi. Stackoverflow melakukan sesuatu: /
Nico S.

304

Solusi yang tepat adalah ini:

- (void)viewDidLoad
{
    [super viewDidLoad];
    UINib *nib = [UINib nibWithNibName:@"ItemCell" bundle:nil];
    [[self tableView] registerNib:nib forCellReuseIdentifier:@"ItemCell"];
}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Create an instance of ItemCell
    PointsItemCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ItemCell"];

    return cell;
}

apakah itu akan merusak aplikasi iOS5? Saya benar-benar tidak pernah melihat UINib
Adam Waite

@AdamWaite Mendaftarkan file NIB berfungsi untuk iOS 5 dan yang lebih baru, sehingga tidak merusak aplikasi iOS 5. Dan UINib bahkan ada sejak iOS 4.
Mecki

Sebagai contoh yang bagus, periksa repo git yang dirujuk dalam jawaban teratas di sini: stackoverflow.com/questions/18746929/…
netigger

39

Daftar

Setelah iOS 7, proses ini disederhanakan menjadi ( swift 3.0 ):

// For registering nib files
tableView.register(UINib(nibName: "MyCell", bundle: Bundle.main), forCellReuseIdentifier: "cell")

// For registering classes
tableView.register(MyCellClass.self, forCellReuseIdentifier: "cell")

( Catatan ) Ini juga dapat dicapai dengan membuat sel di dalam .xibatau .stroyboardfile, sebagai sel prototipe. Jika Anda perlu melampirkan kelas pada mereka, Anda dapat memilih prototipe sel dan menambahkan kelas yang sesuai (tentunya turunan dari UITableViewCell, tentu saja).

Dequeue

Dan nanti, dequeued using ( swift 3.0 ):

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
    let cell : UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)

    cell.textLabel?.text = "Hello"

    return cell
}

Perbedaannya adalah bahwa metode baru ini tidak hanya menghilangkan sel, tetapi juga membuat jika tidak ada (itu berarti Anda tidak perlu melakukan if (cell == nil)shenanigans), dan sel siap digunakan seperti pada contoh di atas.

( Peringatan ) tableView.dequeueReusableCell(withIdentifier:for:)memiliki perilaku baru, jika Anda memanggil yang lain (tanpa indexPath:) Anda mendapatkan perilaku lama, di mana Anda perlu memeriksa nildan membuat instance sendiri, perhatikanUITableViewCell? kembali.

if let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as? MyCellClass
{
    // Cell be casted properly
    cell.myCustomProperty = true
}
else
{
    // Wrong type? Wrong identifier?
}

Dan tentu saja, tipe kelas sel yang terkait adalah yang Anda definisikan dalam file .xib untuk UITableViewCellsubkelas, atau sebagai alternatif, menggunakan metode register lainnya.

Konfigurasi

Idealnya, sel Anda telah dikonfigurasikan dalam hal tampilan dan pemosisian konten (seperti label dan tampilan gambar) pada saat Anda mendaftarkannya, dan pada cellForRowAtIndexPathmetode Anda cukup mengisinya.

Bersama

class MyCell : UITableViewCell
{
    // Can be either created manually, or loaded from a nib with prototypes
    @IBOutlet weak var labelSomething : UILabel? = nil
}

class MasterViewController: UITableViewController 
{
    var data = ["Hello", "World", "Kinda", "Cliche", "Though"]

    // Register
    override func viewDidLoad()
    {
        super.viewDidLoad()

        tableView.register(MyCell.self, forCellReuseIdentifier: "mycell")
        // or the nib alternative
    }

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
    {
        return data.count
    }

    // Dequeue
    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
    {
        let cell = tableView.dequeueReusableCell(withIdentifier: "mycell", for: indexPath) as! MyCell

        cell.labelSomething?.text = data[indexPath.row]

        return cell
    }
}

Dan tentu saja, ini semua tersedia di ObjC dengan nama yang sama.


Berikut adalah versi objC:[self.tableView registerNib:[UINib nibWithNibName:@"BlaBlaTableViewCell" bundle:nil] forCellReuseIdentifier:kCellIdentifier];
Zeb

33

Mengambil jawaban Shawn Craver dan membersihkannya sedikit.

BBCell.h:

#import <UIKit/UIKit.h>

@interface BBCell : UITableViewCell {
}

+ (BBCell *)cellFromNibNamed:(NSString *)nibName;

@end

BBCell.m:

#import "BBCell.h"

@implementation BBCell

+ (BBCell *)cellFromNibNamed:(NSString *)nibName {
    NSArray *nibContents = [[NSBundle mainBundle] loadNibNamed:nibName owner:self options:NULL];
    NSEnumerator *nibEnumerator = [nibContents objectEnumerator];
    BBCell *customCell = nil;
    NSObject* nibItem = nil;
    while ((nibItem = [nibEnumerator nextObject]) != nil) {
        if ([nibItem isKindOfClass:[BBCell class]]) {
            customCell = (BBCell *)nibItem;
            break; // we have a winner
        }
    }
    return customCell;
}

@end

Saya membuat semua subclass UITableViewCell saya dari BBCell, dan kemudian mengganti standar

cell = [[[BBDetailCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"BBDetailCell"] autorelease];

dengan:

cell = (BBDetailCell *)[BBDetailCell cellFromNibNamed:@"BBDetailCell"];

16

Saya menggunakan Metode bentford's # 2 :

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"BDCustomCell"];
    if (cell == nil) {
        // Load the top-level objects from the custom cell XIB.
        NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"BDCustomCell" owner:self options:nil];
        // Grab a pointer to the first object (presumably the custom cell, as that's all the XIB should contain).
        cell = [topLevelObjects objectAtIndex:0];
    }

    return cell;
}

Ini berfungsi, tetapi perhatikan koneksi ke Pemilik File di file .xib UITableViewCell kustom Anda.

Dengan melewati owner:selfdi Anda loadNibNamedpernyataan, Anda menetapkan UITableViewControllersebagai Berkas ini Pemilik Anda UITableViewCell.

Jika Anda menyeret dan melepaskan ke file header di IB untuk mengatur tindakan dan outlet, itu akan mengaturnya sebagai Pemilik File secara default.

Di loadNibNamed:owner:options, kode Apple akan mencoba mengatur properti pada Anda UITableViewController, karena itulah pemiliknya. Tapi Anda tidak memiliki properti-properti yang didefinisikan di sana, sehingga Anda mendapatkan kesalahan tentang menjadi kode-compliant nilai kunci :

*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason:     '[<MyUITableViewController 0x6a383b0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key myLabel.'

Jika suatu Acara dipicu sebagai gantinya, Anda akan mendapatkan NSInvalidArgumentException:

-[MyUITableViewController switchValueDidChange:]: unrecognized selector sent to instance 0x8e9acd0
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[MyUITableViewController switchValueDidChange:]: unrecognized selector sent to instance 0x8e9acd0'
*** First throw call stack:
(0x1903052 0x15eed0a 0x1904ced 0x1869f00 0x1869ce2 0x1904ec9 0x5885c2 0x58855a 0x62db76 0x62e03f 0x77fa6c 0x24e86d 0x18d7966 0x18d7407 0x183a7c0 0x1839db4 0x1839ccb 0x1f8b879 0x1f8b93e 0x585a9b 0xb904d 0x2c75)
terminate called throwing an exceptionCurrent language:  auto; currently objective-c

Solusi yang mudah adalah mengarahkan koneksi Builder Interface Anda di UITableViewCellbukan Pemilik File:

  1. Klik kanan pada File's Owner untuk menarik daftar koneksi
  2. Ambil tangkapan layar dengan Command-Shift-4 (seret untuk memilih area yang akan diambil)
  3. x keluar koneksi dari File's Owner
  4. Klik kanan pada UITableCell di hierarki Objek dan tambahkan kembali koneksi.

Saya punya masalah yang Anda sebutkan, tetapi bagaimana mengarahkan koneksi ke UITableViewCell alih-alih pemilik File? Saya tidak mengerti langkah Anda, misalnya mengapa mengambil tangkapan layar diperlukan? dan ketika saya mengklik tombol add di sebelah outlet, tidak ada yang terjadi
xu huanze

@ xuhuanze Saya menyarankan untuk mengambil tangkapan layar sehingga Anda memiliki catatan tentang hal-hal yang sudah terhubung dengan pemilik File. Kemudian Anda dapat membuat kembali koneksi yang sama. Anda harus menarik dan melepaskan untuk menambahkan koneksi - bukan hanya satu klik.
funroll

Terima kasih banyak, saya punya masalah "kelas ini bukan kode-compliant untuk kunci" dan menyelesaikannya dengan bantuan Anda. Saya ingin memberi tahu orang lain, bahwa Anda juga harus mengubah kelas UITableViewCell Anda ke kelas Anda, yang Anda gunakan sebagai kelas sel khusus.
Denis Kutlubaev

14

Saya telah memutuskan untuk mengirim karena saya tidak suka jawaban ini - semuanya selalu bisa lebih sederhana dan sejauh ini adalah cara paling ringkas yang saya temukan.

1. Bangun Xib Anda di Interface Builder sesuka Anda

  • Tetapkan Pemilik File ke kelas NSObject
  • Tambahkan UITableViewCell dan atur kelasnya ke MyTableViewCellSubclass - jika IB Anda mogok (terjadi dalam Xcode> 4 pada saat penulisan ini), cukup gunakan UIView of do antarmuka dalam Xcode 4 jika Anda masih meletakkannya di sekitar
  • Letakkan subview Anda di dalam sel ini dan lampirkan koneksi IBOutlet Anda ke @interface Anda di .h atau .m (.m adalah pilihan saya)

2. Di subkelas UIViewController atau UITableViewController Anda

@implementation ViewController

static NSString *cellIdentifier = @"MyCellIdentier";

- (void) viewDidLoad {

    ...
    [self.tableView registerNib:[UINib nibWithNibName:@"MyTableViewCellSubclass" bundle:nil] forCellReuseIdentifier:cellIdentifier];
}

- (UITableViewCell*) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    MyTableViewCellSubclass *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];

    ...

    return cell;
}

3. Di MyTableViewCellSubclass Anda

- (id) initWithCoder:(NSCoder *)aDecoder {
    if (self = [super initWithCoder:aDecoder]) {
        ...
    }

    return self;
}

9

Jika Anda menggunakan Interface Builder untuk membuat sel, periksa apakah Anda telah menetapkan Pengidentifikasi di Inspektur. Kemudian periksa apakah itu sama ketika memanggil dequeueReusableCellWithIdentifier.

Saya tidak sengaja lupa untuk menetapkan beberapa pengidentifikasi dalam proyek yang berat, dan perubahan kinerja seperti siang dan malam.


8

Memuat UITableViewCells dari XIB menghemat banyak kode, tetapi biasanya menghasilkan kecepatan pengguliran yang mengerikan (sebenarnya, bukan XIB tetapi penggunaan berlebihan UIViews yang menyebabkan ini).

Saya sarankan Anda melihat ini: Referensi tautan


6

Inilah metode kelas yang telah saya gunakan untuk membuat sel khusus dari XIB:

+ (CustomCell*) createNewCustomCellFromNib {

    NSArray* nibContents = [[NSBundle mainBundle]
                            loadNibNamed:@"CustomCell" owner:self options:NULL];

    NSEnumerator *nibEnumerator = [nibContents objectEnumerator];
    CustomCell *customCell= nil;
    NSObject* nibItem = nil;

    while ( (nibItem = [nibEnumerator nextObject]) != nil) {

        if ( [nibItem isKindOfClass: [CustomCell class]]) {
            customCell = (CustomCell*) nibItem;

            if ([customCell.reuseIdentifier isEqualToString: @"CustomCell"]) {
                break; // we have a winner
            }
            else
                fuelEntryCell = nil;
        }
    }
    return customCell;
}

Kemudian, di XIB, saya menetapkan nama kelas, dan menggunakan kembali pengidentifikasi. Setelah itu, saya bisa memanggil metode itu di pengontrol tampilan saya bukan

[[UITableViewCell] alloc] initWithFrame:]

Ini cukup cepat, dan digunakan dalam dua aplikasi pengiriman saya. Ini lebih dapat diandalkan daripada menelepon [nib objectAtIndex:0], dan dalam pikiran saya setidaknya, lebih dapat diandalkan daripada contoh Stephan Burlot karena Anda dijamin hanya mengambil pandangan dari XIB yang merupakan tipe yang tepat.


5

Solusi yang benar adalah ini

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self.tableView registerNib:[UINib nibWithNibName:@"CustomCell" bundle:[NSBundle mainBundle]] forCellReuseIdentifier:@"CustomCell"];
}

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

4

Muat ulang NIB itu mahal. Lebih baik memuatnya sekali, lalu instantiate objek saat Anda membutuhkan sel. Perhatikan bahwa Anda dapat menambahkan UIImageViews dll ke nib, bahkan beberapa sel, menggunakan metode ini (Apple "registerNIB" iOS5 hanya memungkinkan satu objek tingkat atas - Bug 10580062 "Tabel iOS5 lihat registerNib: terlalu ketat"

Jadi kode saya di bawah ini - Anda membaca di NIB sekali (inisialisasi seperti yang saya lakukan atau di viewDidload - apa pun. Sejak saat itu, Anda instantiate nib ke objek kemudian pilih yang Anda butuhkan. Ini jauh lebih efisien daripada memuat nib lagi dan lagi.

static UINib *cellNib;

+ (void)initialize
{
    if(self == [ImageManager class]) {
        cellNib = [UINib nibWithNibName:@"ImageManagerCell" bundle:nil];
        assert(cellNib);
    }
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *cellID = @"TheCell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
    if(cell == nil) {
        NSArray *topLevelItems = [cellNib instantiateWithOwner:nil options:nil];
        NSUInteger idx = [topLevelItems indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop)
                            {
                                UITableViewCell *cell = (UITableViewCell *)obj;
                                return [cell isKindOfClass:[UITableViewCell class]] && [cell.reuseIdentifier isEqualToString:cellID];
                            } ];
        assert(idx != NSNotFound);
        cell = [topLevelItems objectAtIndex:idx];
    }
    cell.textLabel.text = [NSString stringWithFormat:@"Howdie %d", indexPath.row];

    return cell;
}

4

Lihat ini - http://eppz.eu/blog/custom-uitableview-cell/ - cara yang sangat nyaman menggunakan kelas kecil yang berakhir satu baris dalam implementasi controller:

-(UITableViewCell*)tableView:(UITableView*) tableView cellForRowAtIndexPath:(NSIndexPath*) indexPath
{
    return [TCItemCell cellForTableView:tableView
                          atIndexPath:indexPath
                      withModelSource:self];
}

masukkan deskripsi gambar di sini


3

Cara yang benar untuk melakukannya adalah dengan membuat implementasi subclass, header, dan XIB UITableViewCell. Di XIB hapus tampilan apa pun dan cukup tambahkan sel tabel. Atur kelas sebagai nama subclass UITableViewCell. Untuk pemilik file, buatlah nama kelas subclass UITableViewController. Hubungkan pemilik file ke sel menggunakan outlet tableViewCell.

Dalam file header:

UITableViewCell *_tableViewCell;
@property (assign) IBOutlet UITableViewCell *tableViewCell;

Dalam file implementasi:

@synthesize tableViewCell = _tableViewCell;

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *kCellIdentifier = @"reusableCell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellIdentifier];
    if (cell == nil) {
        [[NSBundle mainBundle] loadNibNamed:kCellIdentifier owner:self options:nil];
        cell = _tableViewCell;
        self.tableViewCell = nil;
    }

    return cell;
}

3

Apa yang saya lakukan untuk ini adalah mendeklarasikan IBOutlet UITableViewCell *celldi kelas controller Anda. Kemudian aktifkan NSBundle loadNibNamedmetode kelas, yang akan memberi makanUITableViewCell ke sel yang dinyatakan di atas.

Untuk xib, saya akan membuat xib kosong dan menambahkan UITableViewCellobjek di IB di mana ia dapat diatur sesuai kebutuhan. Pandangan ini kemudian dihubungkan ke sel IBOutletdi kelas controller.

- (UITableViewCell *)tableView:(UITableView *)table
         cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSLog(@"%@ loading RTEditableCell.xib", [self description] );

    static NSString *MyIdentifier = @"editableCellIdentifier";
    cell = [table dequeueReusableCellWithIdentifier:MyIdentifier];

    if(cell == nil) {
        [[NSBundle mainBundle] loadNibNamed:@"RTEditableCell"
                                      owner:self
                                    options:nil];
    }

    return cell;
}

Tambahan NSBundle loadNibNamed (ADC login)

Artikel cocoawithlove.com Saya bersumber dari konsep (dapatkan aplikasi sampel nomor telepon)


3
  1. Buat AbcViewCellsubclass kelas Anda sendiri yang disesuaikan dari UITableViewCell(Pastikan nama file kelas dan nama file nib Anda sama)

  2. Buat metode kelas ekstensi ini.

    extension UITableViewCell {
        class func fromNib<T : UITableViewCell>() -> T {
            return Bundle.main.loadNibNamed(String(describing: T.self), owner: nil, options: nil)?[0] as! T
        }
    }
  3. Gunakan.

    let cell: AbcViewCell = UITableViewCell.fromNib()


2

Pertama impor file sel kustom Anda #import "CustomCell.h"dan kemudian ubah metode delegasi seperti yang disebutkan di bawah ini:

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

2

Di Swift 4.2 dan Xcode 10

Saya memiliki tiga file sel XIB

di ViewDidLoad daftarkan file XIB Anda seperti ini ...

Ini adalah pendekatan pertama

tableView.register(UINib.init(nibName: "XIBCell", bundle: nil), forCellReuseIdentifier: "cell1")
tableView.register(UINib.init(nibName: "XIBCell2", bundle: nil), forCellReuseIdentifier: "cell2")
//tableView.register(UINib.init(nibName: "XIBCell3", bundle: nil), forCellReuseIdentifier: "cell3")

Pendekatan kedua secara langsung mendaftarkan file XIB di cellForRowAt indexPath:

Ini adalah fungsi delegasi tampilan tabel saya

//MARK: - Tableview delegates
override func numberOfSections(in tableView: UITableView) -> Int {

    return 1
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

    return 6
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    //This is first approach
    if indexPath.row == 0 {//Load first XIB cell
        let placeCell = tableView.dequeueReusableCell(withIdentifier: "cell1") as! XIBCell
        return placeCell
    //Second approach
    } else if indexPath.row == 5 {//Load XIB cell3
        var cell = tableView.dequeueReusableCell(withIdentifier:"cell3") as? XIBCell3
        if cell == nil{
            let arrNib:Array = Bundle.main.loadNibNamed("XIBCell3",owner: self, options: nil)!
            cell = arrNib.first as? XIBCell3
        }

        //ADD action to XIB cell button
        cell?.btn.tag = indexPath.row//Add tag to button
        cell?.btn.addTarget(self, action: #selector(self.bookbtn1(_:)), for: .touchUpInside);//selector

        return cell!
    //This is first approach
    } else {//Load XIB cell2
        let placeCell = tableView.dequeueReusableCell(withIdentifier: "cell2") as! XIBCell2

        return placeCell
    }

}

1

Berikut adalah metode saya untuk itu: Memuat UITableViewCells Kustom dari File XIB ... Namun Metode Lain

Idenya adalah untuk membuat subkelas SampleCell dari UITableViewCelldengan IBOutlet UIView *contentproperti dan properti untuk setiap subview kustom yang perlu Anda konfigurasi dari kode. Kemudian untuk membuat file SampleCell.xib. Di file nib ini, ubah pemilik file ke SampleCell. Tambahkan konten UIViewberukuran sesuai dengan kebutuhan Anda. Tambahkan dan konfigurasikan semua subview (label, tampilan gambar, tombol, dll) yang Anda inginkan. Terakhir, tautkan tampilan konten dan subview ke pemilik file.


1

Berikut ini adalah pendekatan universal untuk mendaftarkan sel di UITableView:

protocol Reusable {
    static var reuseID: String { get }
}

extension Reusable {
    static var reuseID: String {
        return String(describing: self)
    }
}

extension UITableViewCell: Reusable { }

extension UITableView {

func register<T: UITableViewCell>(cellClass: T.Type = T.self) {
    let bundle = Bundle(for: cellClass.self)
    if bundle.path(forResource: cellClass.reuseID, ofType: "nib") != nil {
        let nib = UINib(nibName: cellClass.reuseID, bundle: bundle)
        register(nib, forCellReuseIdentifier: cellClass.reuseID)
    } else {
        register(cellClass.self, forCellReuseIdentifier: cellClass.reuseID)
    }
}

Penjelasan:

  1. Reusableprotokol menghasilkan ID sel dari nama kelasnya. Pastikan Anda mengikuti konvensi tersebut: cell ID == class name == nib name.
  2. UITableViewCellsesuai dengan Reusableprotokol.
  3. UITableView ekstensi abstrak menghilangkan perbedaan dalam mendaftarkan sel melalui nib atau kelas.

Contoh penggunaan:

override func viewDidLoad() {
    super.viewDidLoad()
    let tableView = UITableView()
    let cellClasses: [UITableViewCell.Type] = [PostCell.self, ProfileCell.self, CommentCell.self]
    cellClasses.forEach(tableView.register)
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: PostCell.self.reuseID) as? PostCell
    ...
    return cell
}

0

Saya tidak tahu apakah ada cara kanonik, tapi inilah metode saya:

  • Buat xib untuk ViewController
  • Atur kelas Pemilik File ke UIViewController
  • Hapus tampilan dan tambahkan UITableViewCell
  • Setel Kelas UITableViewCell Anda ke kelas khusus Anda
  • Setel Identifier dari UITableViewCell Anda
  • Setel outlet tampilan view controller Anda ke UITableViewCell Anda

Dan gunakan kode ini:

MyCustomViewCell *cell = (MyCustomViewCell *)[_tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
  UIViewController* c = [[UIViewController alloc] initWithNibName:CellIdentifier bundle:nil];
  cell = (MyCustomViewCell *)c.view;
  [c release];
}

Dalam contoh Anda, menggunakan

[nib objectAtIndex:0]

dapat rusak jika Apple mengubah urutan item dalam xib.


Bagi saya, ini menghasilkan selalu membuat contoh baru. dequeue tampaknya akan kembali nol setiap kali.
aneh

0
 NSString *CellIdentifier = [NSString stringWithFormat:@"cell %ld %ld",(long)indexPath.row,(long)indexPath.section];


    NewsFeedCell *cell = (NewsFeedCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    cell=nil;

    if (cell == nil)
    {
        NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"NewsFeedCell" owner:nil options:nil];

        for(id currentObject in topLevelObjects)
        {
            if([currentObject isKindOfClass:[NewsFeedCell class]])
            {
                cell = (NewsFeedCell *)currentObject;
                break;
            }
        }
}
return cell;

0

Ekstensi ini membutuhkan Xcode7 beta6

extension NSBundle {
    enum LoadViewError: ErrorType {
        case ExpectedXibToExistButGotNil
        case ExpectedXibToContainJustOneButGotDifferentNumberOfObjects
        case XibReturnedWrongType
    }

    func loadView<T>(name: String) throws -> T {
        let topLevelObjects: [AnyObject]! = loadNibNamed(name, owner: self, options: nil)
        if topLevelObjects == nil {
            throw LoadViewError.ExpectedXibToExistButGotNil
        }
        if topLevelObjects.count != 1 {
            throw LoadViewError.ExpectedXibToContainJustOneButGotDifferentNumberOfObjects
        }
        let firstObject: AnyObject! = topLevelObjects.first
        guard let result = firstObject as? T else {
            throw LoadViewError.XibReturnedWrongType
        }
        return result
    }
}

Buat file Xib yang hanya berisi 1 UITableViewCell kustom.

Muat

let cell: BacteriaCell = try NSBundle.mainBundle().loadView("BacteriaCell")

0
 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

            let cellReuseIdentifier = "collabCell"
            var cell:collabCell! = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as? collabCell
            if cell == nil {
                tableView.register(UINib(nibName: "collabCell", bundle: nil), forCellReuseIdentifier: cellReuseIdentifier)
                cell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as! collabCell!
            }


            return cell

}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.