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


Вопрос прост: как вы загружаете пользовательские 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];
}

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

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

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

22 280

22 ответа:

Вот два метода, которыеисходные авторские состояния были рекомендованы инженером 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

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

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

Регистр

После 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 с теми же именами.

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

BBCell.h:

#import <UIKit/UIKit.h>

@interface BBCell : UITableViewCell {
}

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

@end

BBCell.М:

#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"];

Я использовал метод бентфорда #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, так как это владелец. Но у вас нет этих свойств, определенных там, поэтому вы получаете ошибку о том, что key value coding-compliant:

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

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

  1. щелкните правой кнопкой мыши на владельце файла, чтобы открыть список подключений
  2. возьмите захват экрана с помощью команды-Shift-4 (перетащите, чтобы выбрать область для захвата)
  3. x out соединения от владельца файла
  4. щелкните правой кнопкой мыши на ячейке UITableCell в иерархии объектов и повторно добавьте соединения.

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

1. Создайте свой Xib в Interface Builder так, как вам нравится

  • установить владельца файла в класс NSObject
  • добавьте UITableViewCell и установите его класс В MyTableViewCellSubclass -- если ваш IB вылетает (происходит в Xcode > 4 на момент написания этой статьи), просто используйте UIView из do the interface в Xcode 4, если он у вас все еще есть лежа вокруг
  • расположите свои подвиды внутри этой ячейки и присоедините соединения IBOutlet к своему интерфейсу @в этой ячейке .ч или .м (.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;
}

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

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

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

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

Вот метод класса, который я использовал для создания пользовательских ячеек из XIBs:

+ (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 это правильный тип.

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

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

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

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

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

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

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

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

Правильный способ сделать это-создать реализацию подкласса 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;
}

Для этого я объявляю 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 additions loadNibNamed (ADC login)

Статья Cocoawithlove.com я нашел понятие (вам пример приложения "телефонные номера")

Сначала импортируйте пользовательский файл ячеек #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;
}
  1. Создайте свой собственный настроенный класс AbcViewCell подкласс из UITableViewCell (Убедитесь, что имя файла вашего класса и имя файла nib совпадают)

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

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

  • Создайте 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.

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

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

 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;

Это расширение требует 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")

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

}