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

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

вот код, который у меня есть используется:

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.м./h, новый подкласс UITableViewCell и добавить IBOutlets для тех компонентов, которые вы хотите. Затем создайте новый "пустой XIB" файл. Откройте Xib-файл в IB, добавьте UITableViewCell объект, установите его идентификатор в "MyCellIdentifier" и установите его класс В MyCell и добавьте свои компоненты. Наконец, подключите IBOutlets к компонентам. Обратите внимание, что мы не установили владельца файла в IB.

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

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

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, of курс.)

Dequeue

и позже, dequeued с помощью (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"];

я bentford это Способ #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

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

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

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

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

  • установить владельца файла в класс NSObject
  • добавьте UITableViewCell и установите его класс В MyTableViewCellSubclass -- если ваш IB аварийно завершает работу (происходит в Xcode > 4 на момент написания этой статьи), просто используйте UIView интерфейса в Xcode 4, если он все еще лежит вокруг
  • раскройте свои подвиды внутри этой ячейки и прикрепите свои соединения IBOutlet к вашему @интерфейсу в .h или .м.( 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 и т. д. В nib, даже несколько ячеек, используя этот метод (Apple, "registerNIB" iOS5 позволяет только один объект верхнего уровня-ошибка 10580062 "iOS5 tableView registerNib: чрезмерно ограничительный"

Итак, мой код ниже-Вы читаете в NIB один раз (в 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];
}

enter image description here


правильный способ сделать это-создать реализацию подкласса 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 дополнения 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 свойство и свойство для каждого настраиваемого подвида, которое необходимо настроить из кода. Затем создать SampleCell.файл 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

}