Как вы загружаете пользовательские UITableViewCells из файлов Xib?

293

Вопрос прост: как вы загружаете кастом UITableViewCellиз файлов Xib? Это позволяет вам использовать Interface Builder для проектирования ваших ячеек. Ответ очевидно не прост из-за проблем управления памятью. Этот поток упоминает проблему и предлагает решение, но является предварительным выпуском NDA и испытывает недостаток в коде. Вот длинная ветка, в которой обсуждается проблема без четкого ответа.

Вот код, который я использовал:

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

Чтобы использовать этот код, создайте MyCell.m / .h, новый подкласс UITableViewCellи добавьте IBOutletsдля компонентов, которые вы хотите. Затем создайте новый файл «Пустой XIB». Откройте файл Xib в IB, добавьте UITableViewCellобъект, установите для его идентификатора «MyCellIdentifier», установите для его класса значение MyCell и добавьте свои компоненты. Наконец, подключите IBOutletsк компонентам. Обратите внимание, что мы не установили владельца файла в IB.

Другие методы рекомендуют устанавливать владельца файла и предупреждать об утечках памяти, если Xib не загружается через дополнительный фабричный класс. Я проверил вышеизложенное в разделе «Инструменты / утечки» и не обнаружил утечек памяти.

Так каков канонический способ загрузки ячеек из Xibs? Мы устанавливаем владельца файла? Нужен ли нам завод? Если да, то как выглядит код фабрики? Если есть несколько решений, давайте выясним плюсы и минусы каждого из них ...

DrGary
источник
2
Может ли кто-то отредактировать тему, чтобы фактически задать вопрос, например, «Как вы загружаете пользовательские UITableViewCells из файлов Xib?» (Проигнорируйте, если это просто невозможно на стеке потока).
Стивен Фишер
1
Для iOS 5 и выше это решение: stackoverflow.com/questions/15591364/… , которое совпадает с решением giuseppe.
Мэтт Беккер
Краткое примечание, более простой ответ (среда 2013 г.) здесь ответ stackoverflow.com/questions/15378788/… jamihash
Fattie

Ответы:

288

Вот два метода, которые автор оригинальной версии рекомендовал инженеру IB .

Смотрите фактический пост для более подробной информации. Я предпочитаю метод № 2, так как он кажется более простым.

Способ № 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;
}

Способ № 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;
}

Обновление (2014): Метод № 2 все еще действителен, но для него больше нет документации. Раньше это было в официальных документах, но теперь удалено в пользу раскадровок.

Я разместил рабочий пример на Github:
https://github.com/bentford/NibTableCellExample

редактировать для 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
источник
1
Для метода 1, вы не должны делать что-то вроде «cell = (BDCustomCell *) [[временныйController.view retain] autorelease];» ячейка не освобождается при освобождении временного контроллера?
Тод Каннингем
Гектометр Документация, в которой говорится о # 2, все еще говорит вам, чтобы установить владельца ячейки в файле XIB на известный класс контроллера. Возможно, не имеет значения, когда вы устанавливаете владельца во время загрузки.
Оскар
@OscarGoldman Владелец ячейки в файле XIB - это класс (т. Е. Тип владельца.) Владелец ячейки в loadNibNamed: owner: options: является объектом типа, указанного в XIB.
Бентфорд
2
@CoolDocMan Вариант №2 все еще работает. Проблема скорее всего с пером. Вот пример: github.com/bentford/NibTableCellExample
Бентфорд
2
Почему этот супер старый код занимает столь высокое место? Stackoverflow сделать что-то: /
Нико С.
304

Правильное решение таково:

- (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
источник
это сломает iOS5 приложения? Я действительно никогда не видел UINib
Адам Уэйт
@AdamWaite Регистрация файлов NIB работает для iOS 5 и более поздних версий, поэтому не нарушает работу приложений iOS 5. И UINib даже существует со времен iOS 4.
Mecki
Для хорошего примера, проверьте
репозиторий
39

регистр

После iOS 7 этот процесс был упрощен до ( 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")

( Примечание ) Этого также можно достичь, создавая ячейки в файлах .xibили .stroyboard, как ячейки прототипа. Если вам нужно присоединить к ним класс, вы можете выбрать прототип ячейки и добавить соответствующий класс ( UITableViewCellконечно, он должен быть потомком ).

Dequeue

И позже, снято с очереди ( 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
}

Разница в том, что этот новый метод не только удаляет ячейку, но и создает ее, если она не существует (это означает, что вам не нужно делать if (cell == nil)махинации), и ячейка готова к использованию, как в примере выше.

( Предупреждение ) tableView.dequeueReusableCell(withIdentifier:for:)имеет новое поведение, если вы вызываете другое (без indexPath:), вы получаете старое поведение, в котором вам нужно проверить nilи выполнить его экземпляр самостоятельно, обратите внимание на UITableViewCell?возвращаемое значение.

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

И, конечно же, тип связанного класса ячейки - это тот, который вы определили в файле .xib для UITableViewCellподкласса или, альтернативно, с помощью другого метода регистра.

конфигурация

В идеале ваши ячейки уже были настроены с точки зрения внешнего вида и позиционирования контента (например, надписей и представлений изображений) к тому времени, когда вы их зарегистрировали, и к cellForRowAtIndexPathспособу, который вы просто заполняете.

Все вместе

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

И, конечно, все это доступно в ObjC с такими же именами.

Жестяная банка
источник
Вот версия objC:[self.tableView registerNib:[UINib nibWithNibName:@"BlaBlaTableViewCell" bundle:nil] forCellReuseIdentifier:kCellIdentifier];
Зеб
33

Взял ответ Шона Крейвера и немного его почистил.

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

Я делаю все мои подклассы UITableViewCell BBCell, а затем заменяю стандарт

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

с участием:

cell = (BBDetailCell *)[BBDetailCell cellFromNibNamed:@"BBDetailCell"];
vilcsak
источник
16

Я использовал метод Бентфорда № 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;
}

Это работает, но следите за соединениями с владельцем файла в вашем пользовательском файле UITableViewCell .xib.

Передавая owner:selfсвое loadNibNamedзаявление, вы устанавливаете в UITableViewControllerкачестве владельца файла вашего UITableViewCell.

Если вы перетащите файл заголовка в IB, чтобы настроить действия и выходы, он по умолчанию установит их в качестве владельца файла.

В loadNibNamed:owner:optionsкоде Apple будет пытаться установить свойства на ваш UITableViewController, так как это владелец. Но у вас нет этих свойств, определенных там, поэтому вы получаете ошибку о том, что значение ключа соответствует кодированию :

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

Если событие инициируется вместо этого, вы получите 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

Простой обходной путь - указать ваши соединения Interface Builder UITableViewCellвместо владельца файла:

  1. Щелкните правой кнопкой мыши по Владельцу файла, чтобы открыть список подключений.
  2. Сделайте снимок экрана с помощью Command-Shift-4 (перетащите, чтобы выбрать область для захвата)
  3. вычеркнуть соединения у владельца файла
  4. Щелкните правой кнопкой мыши UITableCell в иерархии объектов и заново добавьте соединения.
funroll
источник
У меня была проблема, которую вы упомянули, но как указать соединения с UITableViewCell вместо владельца файла? Я не понимаю ваши шаги, например, зачем делать скриншот? и когда я нажал кнопку добавления рядом с розеткой, ничего не
произошло
@xuhuanze Я предложил сделать скриншот, чтобы у вас была запись того, с чем владелец файла уже был связан. Затем вы можете воссоздать те же самые соединения. Вам нужно перетаскивать, чтобы добавить соединения, а не просто щелкнуть мышью.
funroll
Большое спасибо, у меня была проблема «этот класс не совместим с ключом для ключа», и я решил ее с вашей помощью. Я хочу сказать другим, что вы также должны изменить класс вашего UITableViewCell на свой класс, который вы используете в качестве пользовательского класса ячейки.
Денис Кутлубаев
14

Я решил опубликовать, так как мне не нравится ни один из этих ответов - вещи всегда могут быть более простыми, и это, безусловно, самый лаконичный способ, который я нашел.

1. Создайте свою Xib в Интерфейсном Разработчике, как вам нравится

  • Установите для владельца файла класс NSObject
  • Добавьте UITableViewCell и установите его класс в MyTableViewCellSubclass - если ваш IB падает (случается в Xcode> 4 на момент написания этой статьи), просто используйте UIView или do interface в Xcode 4, если он все еще у вас лежит.
  • Разместите ваши подпредставления внутри этой ячейки и присоедините ваши соединения IBOutlet к вашему @interface в .h или .m (.m - мое предпочтение)

2. В вашем подклассе UIViewController или 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. В вашем MyTableViewCellSubclass

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

    return self;
}
webstersx
источник
9

Если вы используете Interface Builder для создания ячеек, убедитесь, что вы установили Идентификатор в Инспекторе. Затем проверьте, что это то же самое при вызове dequeueReusableCellWithIdentifier.

Я случайно забыл установить некоторые идентификаторы в настольном проекте, и изменение производительности было похоже на день и ночь.

Алекс Р. Янг
источник
8

Загрузка UITableViewCells из XIBs экономит много кода, но обычно приводит к ужасной скорости прокрутки (на самом деле это не XIB, а чрезмерное использование UIViews, которое вызывает это).

Я предлагаю вам взглянуть на это: ссылка ссылка

Can Berk Güder
источник
6

Вот метод класса, который я использовал для создания пользовательских ячеек из 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;
}

Затем в XIB я устанавливаю имя класса и повторно использую идентификатор. После этого я могу просто вызвать этот метод в моем контроллере представления вместо

[[UITableViewCell] alloc] initWithFrame:]

Это достаточно быстро и используется в двух моих приложениях доставки. Это более надежно, чем вызов [nib objectAtIndex:0], и, по крайней мере, на мой взгляд, более надежно, чем пример Стефана Бурло, потому что вы гарантированно получите только представление из XIB, которое является правильным типом.

Шон Крейвер
источник
5

Правильное решение это

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

Перезагрузка NIB стоит дорого. Лучше загрузить его один раз, а затем создавать объекты, когда вам нужна ячейка. Обратите внимание, что вы можете добавить UIImageViews и т. Д. К пиру, даже к нескольким ячейкам, используя этот метод (Apple «registerNIB» iOS5 допускает только один объект верхнего уровня - Ошибка 10580062 «iOS5 tableView registerNib: чрезмерно ограничительный»)

Итак, мой код ниже - вы читаете в NIB один раз (при инициализации, как я, или в viewDidload - что угодно. С тех пор вы создаете экземпляр nib в объектах, а затем выбираете тот, который вам нужен. Это намного эффективнее, чем загрузка nib снова и снова.

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

Проверьте это - http://eppz.eu/blog/custom-uitableview-cell/ - действительно удобный способ использования крошечного класса, который заканчивается одной строкой в ​​реализации контроллера:

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

введите описание изображения здесь

Джери Борбас
источник
3

Правильный способ сделать это - создать реализацию подкласса UITableViewCell, заголовок и XIB. В XIB удалите все представления и просто добавьте ячейку таблицы. Установите класс как имя подкласса UITableViewCell. Для владельца файла сделайте его именем класса подкласса UITableViewController. Подключите владельца файла к ячейке, используя выход tableViewCell.

В заголовочном файле:

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

В файле реализации:

@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

Что я делаю для этого, объявляю IBOutlet UITableViewCell *cellв вашем классе контроллера. Затем вызовите NSBundle loadNibNamedметод класса, который передаст UITableViewCellячейку, объявленную выше.

Для xib я создам пустой xib и добавлю UITableViewCellобъект в IB, где он может быть настроен по мере необходимости. Это представление затем подключается к ячейке IBOutletв классе контроллера.

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

Дополнения NSBundle loadNibNamed (вход в АЦП)

Статья cocoawithlove.com, из которой я получил эту концепцию (пример приложения для телефонных номеров)

Райан Тауншенд
источник
3
  1. Создайте свой собственный настраиваемый AbcViewCellподкласс класса из UITableViewCell(убедитесь, что ваше имя файла класса и имя файла пера совпадают)

  2. Создайте этот метод класса расширения.

    extension UITableViewCell {
        class func fromNib<T : UITableViewCell>() -> T {
            return Bundle.main.loadNibNamed(String(describing: T.self), owner: nil, options: nil)?[0] as! T
        }
    }
  3. Используй это.

    let cell: AbcViewCell = UITableViewCell.fromNib()

Уильям Ху
источник
2

Сначала импортируйте свой пользовательский файл ячейки, #import "CustomCell.h"а затем измените метод делегата, как указано ниже:

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

В Swift 4.2 и Xcode 10

У меня есть три файла ячеек XIB

во ViewDidLoad зарегистрируйте свои файлы XIB следующим образом ...

Это первый подход

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

Второй подход - напрямую зарегистрировать файлы XIB в cellForRowAt indexPath:

Это мои функции делегата таблицы

//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
источник
1

Вот мой метод для этого: загрузка пользовательских UITableViewCells из файлов XIB ... еще один метод

Идея состоит в том, чтобы создать подкласс SampleCell UITableViewCellсо IBOutlet UIView *contentсвойством и свойством для каждого настраиваемого подпредставления, которое необходимо настроить из кода. Затем создать файл SampleCell.xib. В этом nib-файле измените владельца файла на SampleCell. Добавьте контент, UIViewразмер которого соответствует вашим потребностям. Добавьте и настройте все необходимые подпредставления (метка, изображения, кнопки и т. Д.). Наконец, свяжите представление содержимого и подпредставления с владельцем файла.

MonsieurDart
источник
1

Вот универсальный подход для регистрации ячеек в 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)
    }
}

Объяснение:

  1. ReusableПротокол генерирует идентификатор ячейки из имени класса. Убедитесь, что вы следуете соглашению cell ID == class name == nib name.
  2. UITableViewCellсоответствует Reusableпротоколу.
  3. UITableView Расширение абстрагирует разницу в регистрации ячеек с помощью пера или класса.

Пример использования:

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

Я не знаю, есть ли канонический путь, но вот мой метод:

  • Создайте xib для ViewController
  • Установите класс владельца файла в UIViewController
  • Удалить вид и добавить UITableViewCell
  • Установите класс вашего UITableViewCell в свой пользовательский класс
  • Установите идентификатор вашего UITableViewCell
  • Установите выход вашего представления контроллера представления на ваш UITableViewCell

И используйте этот код:

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

В вашем примере, используя

[nib objectAtIndex:0]

может сломаться, если Apple изменит порядок элементов в xib.

Стефан Бурло
источник
Для меня это всегда приводит к созданию нового экземпляра. Кажется, что dequeue каждый раз возвращает ноль.
странно
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
источник
0

Для этого расширения требуется 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
    }
}

Создайте файл Xib, который содержит только 1 пользовательский UITableViewCell.

Загрузите это.

let cell: BacteriaCell = try NSBundle.mainBundle().loadView("BacteriaCell")
neoneye
источник
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

}
Хитеш Чаухан
источник