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

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

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

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

Ответы

Ответ 1

Вот два метода, которые автор оригинальной версии рекомендовал инженеру 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 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
}

Ответ 2

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

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

Ответ 3

Регистрация

После 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

И позже, 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) shenanigans), и ячейка готова к использованию так же, как в пример выше.

(Предупреждение) 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 с одинаковыми именами.

Ответ 4

Взял Shawn Craver и немного почистил его.

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

Ответ 5

Я использовал 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 all the XIB should contain).
        cell = [topLevelObjects objectAtIndex:0];
    }

    return cell;
}

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

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

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

В 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

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

  • Щелкните правой кнопкой мыши на File Owner, чтобы найти список подключений.
  • Сделайте захват экрана с помощью Command-Shift-4 (перетащите, чтобы выбрать область для захвата)
  • x вывести соединения с File Owner
  • Щелкните правой кнопкой мыши по UITableCell в иерархии объектов и снова добавьте соединения.

Ответ 6

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

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

  • Установить владельца файла классу NSObject
  • Добавьте UITableViewCell и установите его класс в MyTableViewCellSubclass - если ваш IB сбой (происходит в Xcode > 4 на момент написания этой статьи), просто используйте UIView для интерфейса в 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;
}

Ответ 7

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

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

Ответ 8

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

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

Ответ 9

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

Ответ 10

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

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

Ответ 11

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

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

Ответ 12

Проверьте это - 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

Ответ 13

Что я делаю для этого, объявляю 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)

статья cocoawithlove.com Я получил эту концепцию от (пример с образцами телефонов)

Ответ 14

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

Ответ 15

Сначала импортируйте свой пользовательский файл соты #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;
}

Ответ 16

  • Создайте свой собственный подкласс класса AbcViewCell из UITableViewCell (убедитесь, что имя файла вашего файла и имя файла nib совпадают)

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

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

    let cell: AbcViewCell = UITableViewCell.fromNib()

Ответ 17

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

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

Ответ 18

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

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

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
}

Ответ 19

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

}

Ответ 20

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

  • Создайте xib для ViewController
  • Задайте класс File Owner для 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.

Ответ 21

 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;

Ответ 22

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

Ответ 23

 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

}