Jak ładujesz niestandardowe komórki UITableViewCell z plików Xib?

293

Pytanie jest proste: jak ładować niestandardowe UITableViewCellpliki Xib? Pozwala to na użycie Konstruktora interfejsów do zaprojektowania komórek. Odpowiedź najwyraźniej nie jest prosta ze względu na problemy z zarządzaniem pamięcią. Wątek wspomina o tym problemie i sugeruje rozwiązanie, ale jest wydany przed wydaniem NDA i nie zawiera kodu. Oto długi wątek, który omawia ten problem bez ostatecznej odpowiedzi.

Oto kod, którego użyłem:

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

Aby użyć tego kodu, utwórz MyCell.m / .h, nową podklasę UITableViewCelli dodaj IBOutletsodpowiednie komponenty. Następnie utwórz nowy plik „Pusty XIB”. Otwórz plik Xib w IB, dodaj UITableViewCellobiekt, ustaw jego identyfikator na „MyCellIdentifier”, ustaw klasę na MyCell i dodaj swoje komponenty. Na koniec podłącz IBOutletsdo komponentów. Pamiętaj, że nie ustawiliśmy właściciela pliku w IB.

Inne metody zalecają ustawienie właściciela pliku i ostrzeżenie przed wyciekiem pamięci, jeśli Xib nie zostanie załadowany przez dodatkową klasę fabryczną. Testowałem powyższe pod Instruments / Leaks i nie zauważyłem wycieków pamięci.

Więc jaki jest kanoniczny sposób ładowania komórek z Xib? Czy ustawiamy właściciela pliku? Czy potrzebujemy fabryki? Jeśli tak, to jak wygląda kod dla fabryki? Jeśli istnieje wiele rozwiązań, wyjaśnijmy zalety i wady każdego z nich ...

DrGary
źródło
2
Czy ktoś może edytować temat, aby faktycznie zadać pytanie, tj. „Jak ładujesz niestandardowe komórki UITableViewCell z plików Xib?” (Zignoruj, jeśli nie jest to możliwe przy przepełnieniu stosu.)
Steven Fisher,
1
W przypadku systemu iOS 5 i nowszych jest to rozwiązanie: stackoverflow.com/questions/15591364/... , które jest takie samo jak rozwiązanie giuseppe.
Matt Becker
Szybka notatka, prostsze (środowisko 2013) odpowiedz tutaj stackoverflow.com/questions/15378788/... jamihash
Fattie

Odpowiedzi:

288

Oto dwie metody, które według oryginalnego autora były zalecane przez inżyniera IB .

Zobacz rzeczywisty post, aby uzyskać więcej informacji. Wolę metodę nr 2, ponieważ wydaje się to prostsze.

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

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

Aktualizacja (2014): Metoda nr 2 jest nadal aktualna, ale nie ma już jej dokumentacji. Kiedyś był w oficjalnych dokumentach, ale teraz został usunięty na rzecz scenariuszy.

Opublikowałem działający przykład na Github:
https://github.com/bentford/NibTableCellExample

edycja dla 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
}
Bentford
źródło
1
W przypadku metody 1 nie powinieneś robić czegoś takiego jak „cell = (BDCustomCell *) [[temporController.view retain] autorelease];” więc komórka nie zostaje zwolniona po zwolnieniu kontrolera tymczasowego?
Tod Cunningham
Hm Dokumentacja, która mówi o # 2, wciąż mówi o ustawieniu właściciela komórki w pliku XIB na znaną klasę kontrolera. Być może nie ma znaczenia, kiedy ustawisz właściciela podczas ładowania.
Oscar,
@OscarGoldman Właścicielem komórki w pliku XIB jest klasa (tj. Typ właściciela). Właściciel komórki w loadNibNamed: właściciel: opcje: jest obiektem typu określonego w XIB.
Bentford
2
@CoolDocMan Opcja # 2 nadal działa. Problem najprawdopodobniej dotyczy stalówki. Oto przykład: github.com/bentford/NibTableCellExample
bentford
2
Dlaczego ten bardzo stary kod ma tak wysoką pozycję? Stackoverflow zrób coś: /
Nico S.
304

Właściwe rozwiązanie to:

- (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;
}
Giuseppe
źródło
czy to zepsuje aplikacje na iOS5? Naprawdę nigdy nie widziałem UINib
Adam Waite
@AdamWaite Rejestracja plików NIB działa na iOS 5 i nowszych, więc nie psuje aplikacji na iOS 5. A UINib istnieje nawet od iOS 4.
Mecki
Dla dobrego przykładu sprawdź repozytorium git, o którym mowa w górnej odpowiedzi tutaj: stackoverflow.com/questions/18746929/…
netigger
39

Zarejestrować

Po iOS 7 proces ten został uproszczony do ( 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")

( Uwaga ) Można to również osiągnąć, tworząc komórki w plikach .xiblub .stroyboardjako komórki prototypowe. Jeśli musisz dołączyć do nich klasę, możesz wybrać prototyp komórki i dodać odpowiednią klasę ( UITableViewCelloczywiście musi to być potomek ).

Dequeue

A później odkażone przy użyciu ( 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
}

Różnica polega na tym, że ta nowa metoda nie tylko usuwa z komórki kolejkę, ale tworzy ją również, jeśli nie istnieje (co oznacza, że ​​nie musisz wykonywać if (cell == nil)shenaniganów), a komórka jest gotowa do użycia, tak jak w powyższym przykładzie.

Ostrzeżenie ( ostrzeżenie ) tableView.dequeueReusableCell(withIdentifier:for:)ma nowe zachowanie, jeśli wywołasz drugie (bez indexPath:), otrzymasz stare zachowanie, w którym musisz sam sprawdzić nili zaimplementować je, zwróć uwagę na UITableViewCell?wartość zwracaną.

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

I oczywiście typ powiązanej klasy komórki to ta, którą zdefiniowałeś w pliku .xib dla UITableViewCellpodklasy lub alternatywnie przy użyciu innej metody register.

Konfiguracja

Idealnie byłoby, gdyby Twoje komórki były już skonfigurowane pod względem wyglądu i pozycjonowania treści (takich jak etykiety i widoki obrazów) do czasu ich zarejestrowania, i cellForRowAtIndexPathmetodą, którą po prostu wypełniasz.

Wszyscy razem

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

Oczywiście wszystko to jest dostępne w ObjC o tych samych nazwach.

Mogą
źródło
Oto wersja objC:[self.tableView registerNib:[UINib nibWithNibName:@"BlaBlaTableViewCell" bundle:nil] forCellReuseIdentifier:kCellIdentifier];
Zeb
33

Wziął odpowiedź Shawna Cravera i trochę ją oczyścił.

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

Tworzę wszystkie podklasy BBCell mojej UITableViewCell, a następnie zastępuję standard

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

z:

cell = (BBDetailCell *)[BBDetailCell cellFromNibNamed:@"BBDetailCell"];
Wilcsak
źródło
16

Użyłem metody bentforda # 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;
}

Działa, ale uważaj na połączenia z właścicielem pliku w niestandardowym pliku .xib UITableViewCell.

Przekazując owner:selfswoje loadNibNamedoświadczenie, ustawiasz UITableViewControllerwłaściciela pliku jako swojego UITableViewCell.

Przeciągnięcie i upuszczenie pliku nagłówka w IB w celu skonfigurowania akcji i ujść spowoduje, że domyślnie skonfiguruje je jako Właściciel pliku.

W loadNibNamed:owner:optionskod Apple spróbuje ustawić właściwości na twoim UITableViewController, ponieważ to jest właściciel. Ale nie masz tam zdefiniowanych właściwości, więc pojawia się błąd dotyczący zgodności kodowania wartości klucza :

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

Jeśli zamiast tego zostanie wywołane zdarzenie, otrzymasz wyjątek 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

Łatwym obejściem jest skierowanie połączeń Konstruktora interfejsów UITableViewCellna właściciela pliku:

  1. Kliknij prawym przyciskiem myszy właściciela pliku, aby wyświetlić listę połączeń
  2. Zrób zrzut ekranu za pomocą Command-Shift-4 (przeciągnij, aby wybrać obszar do przechwycenia)
  3. x poza połączeniami od właściciela pliku
  4. Kliknij prawym przyciskiem myszy UITableCell w hierarchii obiektów i ponownie dodaj połączenia.
funroll
źródło
Miałem problem, o którym wspomniałeś, ale jak wskazać połączenia na UITableViewCell zamiast na właściciela pliku? Nie rozumiem twoich kroków, np. Dlaczego potrzebny jest zrzut ekranu? a kiedy kliknąłem przycisk dodawania obok gniazdka, nic się nie dzieje
xu huanze
@xuhuanze Zasugerowałem zrobienie zrzutu ekranu, abyś miał dostęp do danych, z którymi właściciel pliku był już połączony. Następnie możesz ponownie utworzyć te same połączenia. Przeciągnij i upuść, aby dodać połączenia - nie tylko jednym kliknięciem.
funroll
Wielkie dzięki, miałem problem z tym, że ta klasa nie jest zgodna z kluczowym kodowaniem wartości klucza i rozwiązałem go z waszej pomocy. Chcę powiedzieć innym, że należy również zmienić klasę UITableViewCell na klasę, której używasz jako niestandardowej klasy komórek.
Denis Kutlubaev,
14

Zdecydowałem się opublikować, ponieważ nie podoba mi się żadna z tych odpowiedzi - wszystko może być zawsze prostsze i jest to najbardziej zwięzły sposób, jaki znalazłem.

1. Zbuduj swój Xib w Konstruktorze interfejsów, jak chcesz

  • Ustaw właściciela pliku na klasę NSObject
  • Dodaj UITableViewCell i ustaw jego klasę na MyTableViewCellSubclass - jeśli twój IB ulega awarii (dzieje się to w Xcode> 4 w momencie pisania), po prostu użyj UIView zrób interfejs w Xcode 4, jeśli nadal go masz
  • Ułóż swoje widoki podrzędne w tej komórce i dołącz połączenia IBOutlet do interfejsu @ w .h lub .m (.m to moja preferencja)

2. W podklasie UIViewController lub UITableViewController

@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. W twojej MyTableViewCellSubclass

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

    return self;
}
webstersx
źródło
9

Jeśli używasz Konstruktora interfejsów do tworzenia komórek, sprawdź, czy ustawiłeś Identyfikator w Inspektorze. Następnie sprawdź, czy to samo podczas wywoływania dequeueReusableCellWithIdentifier.

Przypadkowo zapomniałem ustawić niektóre identyfikatory w dużym projekcie, a zmiana wydajności była jak noc i dzień.

Alex R. Young
źródło
8

Ładowanie UITableViewCells z XIBs oszczędza dużo kodu, ale zwykle skutkuje okropną prędkością przewijania (tak naprawdę to nie XIB, ale nadmierne użycie UIViews powoduje to).

Proponuję spojrzeć na to: Odnośnik do linku

Czy Berk Güder
źródło
6

Oto metoda klasy, której używałem do tworzenia niestandardowych komórek z 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;
}

Następnie w XIB ustawiam nazwę klasy i ponownie używam identyfikatora. Następnie mogę po prostu wywołać tę metodę w moim kontrolerze widoku zamiast

[[UITableViewCell] alloc] initWithFrame:]

Jest wystarczająco szybki i jest używany w dwóch moich aplikacjach wysyłkowych. Jest bardziej niezawodny niż dzwonienie [nib objectAtIndex:0], a przynajmniej moim zdaniem bardziej niezawodny niż przykład Stephana Burlota, ponieważ masz gwarancję, że złapiesz tylko widok z XIB, który jest odpowiedniego typu.

Shawn Craver
źródło
5

Prawidłowe rozwiązanie jest to

- (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; 
    }
Hamiz Ahmed
źródło
4

Ponowne załadowanie NIB jest kosztowne. Lepiej go załaduj raz, a następnie utwórz instancję obiektów, gdy potrzebujesz komórki. Pamiętaj, że możesz dodać UIImageViews itp. Do stalówki, nawet wielu komórek, używając tej metody (Apple „registerNIB” iOS5 pozwala tylko na jeden obiekt najwyższego poziomu - Bug 10580062 „Tabela iOS5 registerNib: nadmiernie restrykcyjny”

Więc mój kod jest poniżej - czytasz raz w NIB (inicjuj tak jak ja lub w viewDidload - cokolwiek. Od tego momentu tworzysz stalówkę w obiektach, a następnie wybierasz ten, którego potrzebujesz. Jest to o wiele bardziej wydajne niż ładowanie stalówki raz po raz.

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;
}
David H.
źródło
4

Sprawdź to - http://eppz.eu/blog/custom-uitableview-cell/ - naprawdę wygodny sposób za pomocą małej klasy, która kończy się jedną linią w implementacji kontrolera:

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

wprowadź opis zdjęcia tutaj

Geri Borbás
źródło
3

Prawidłowym sposobem na to jest utworzenie implementacji podklasy UITableViewCell, nagłówka i XIB. W XIB usuń wszelkie widoki i po prostu dodaj komórkę tabeli. Ustaw klasę jako nazwę podklasy UITableViewCell. Dla właściciela pliku nadaj mu nazwę klasy podklasy UITableViewController. Podłącz właściciela pliku do komórki za pomocą gniazdka tableViewCell.

W pliku nagłówkowym:

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

W pliku implementacyjnym:

@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;
}
Cameron Lowell Palmer
źródło
3

To, co robię, to zadeklarowanie IBOutlet UITableViewCell *cellw klasie kontrolera. Następnie wywołaj NSBundle loadNibNamedmetodę klasy, która nakarmi UITableViewCellkomórkę zadeklarowaną powyżej.

Dla xib utworzę pusty xib i dodam UITableViewCellobiekt w IB, gdzie można go skonfigurować w razie potrzeby. Ten widok jest następnie połączony z komórką IBOutletw klasie kontrolera.

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

Dodatki NSBundle loadNibNamed (logowanie ADC)

Artykuł cocoawithlove.com, z którego zacząłem czerpać pojęcie

Ryan Townshend
źródło
3
  1. Utwórz własną niestandardową AbcViewCellpodklasę klasy z UITableViewCell(Upewnij się, że nazwa pliku klasy i nazwa pliku stalówki są takie same)

  2. Utwórz tę metodę klasy rozszerzeń.

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

    let cell: AbcViewCell = UITableViewCell.fromNib()

William Hu
źródło
2

Najpierw zaimportuj niestandardowy plik komórki, #import "CustomCell.h"a następnie zmień metodę delegowania, jak wspomniano poniżej:

- (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
źródło
2

W Swift 4.2 i Xcode 10

Mam trzy pliki komórek XIB

w ViewDidLoad zarejestruj swoje pliki XIB w ten sposób ...

To jest pierwsze podejście

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")

Drugie podejście bezpośrednio rejestruje pliki XIB w cellForRowAt indexPath:

To są moje funkcje delegowania widoku tabeli

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

}
iOS
źródło
1

Oto moja metoda: ładowanie niestandardowych komórek UITableViewCell z plików XIB… Jeszcze inna metoda

Chodzi o to, aby utworzyć podklasę SampleCell UITableViewCellz IBOutlet UIView *contentwłaściwością i właściwością dla każdego niestandardowego podview, który należy skonfigurować z kodu. Następnie, aby utworzyć plik SampleCell.xib. W tym pliku stalówki zmień właściciela pliku na SampleCell. Dodaj treść UIViewdostosowaną do twoich potrzeb. Dodaj i skonfiguruj wszystkie widoki podrzędne (etykiety, widoki obrazów, przyciski itp.), Które chcesz. Na koniec połącz widok zawartości i widoki podrzędne z właścicielem pliku.

MonsieurDart
źródło
1

Oto uniwersalne podejście do rejestrowania komórek w 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)
    }
}

Wyjaśnienie:

  1. Reusableprotokół generuje identyfikator komórki na podstawie nazwy klasy. Upewnij się, że zgodnie z konwencją: cell ID == class name == nib name.
  2. UITableViewCelljest zgodny z Reusableprotokołem.
  3. UITableView rozszerzenie abstrahuje różnicę w rejestrowaniu komórek za pomocą stalówki lub klasy.

Przykład użycia:

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
}
Vadim Bulavin
źródło
0

Nie wiem, czy istnieje sposób kanoniczny, ale oto moja metoda:

  • Utwórz Xib dla ViewController
  • Ustaw klasę File Owner na UIViewController
  • Usuń widok i dodaj UITableViewCell
  • Ustaw klasę UITableViewCell na klasę niestandardową
  • Ustaw identyfikator swojego UITableViewCell
  • Ustaw ujście widoku kontrolera widoku na UITableViewCell

I użyj tego kodu:

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

W twoim przykładzie za pomocą

[nib objectAtIndex:0]

może się zepsuć, jeśli Apple zmieni kolejność elementów w Xib.

Stephan Burlot
źródło
Dla mnie skutkuje to zawsze tworzeniem nowej instancji. Wydaje się, że dequeue za każdym razem wraca do zera.
dziwne
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;
2014
źródło
0

To rozszerzenie wymaga 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
    }
}

Utwórz plik Xib, który zawiera tylko 1 niestandardową UITableViewCell.

Załaduj To.

let cell: BacteriaCell = try NSBundle.mainBundle().loadView("BacteriaCell")
neoneye
źródło
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

}
Hitesh Chauhan
źródło