Як ви завантажуєте власні 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до компонентів. Зауважте, що ми не встановили власника файлу в ІБ.

Інші методи виступають за встановлення власника файлу та попереджають про витоки пам'яті, якщо Xib не завантажується через додатковий заводський клас. Я перевірив вищезазначене в розділі "Інструменти / витоки" і не побачив витоків пам'яті.

То який канонічний спосіб завантаження комірок з Xibs? Чи встановити власника файлу? Чи потрібна нам фабрика? Якщо так, то як виглядає код заводу? Якщо є кілька рішень, давайте уточнимо плюси і мінуси кожного з них ...


2
Чи може хтось редагувати тему, щоб насправді задати питання, тобто "Як ви завантажуєте власні UITableViewCells з файлів Xib?" (Ігноруйте, якщо це просто неможливо в stackoverflow.)
Стівен Фішер

1
Для iOS 5 та новіших версій це рішення: stackoverflow.com/questions/15591364/… , що таке саме, як рішення giuseppe.
Метт Бекер

Коротка примітка, простіше (2013 середа) відповідь тут stackoverflow.com/questions/15378788 / ... jamihash
Fattie

Відповіді:


288

Ось два методи, про які автор заявляв, рекомендував інженер ІБ .

Детальнішу інформацію див. У фактичній публікації. Я вважаю за краще метод №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
}

1
Для методу 1, чи не слід робити щось на кшталт "cell = (BDCustomCell *) [[тимчасовийController.view зберегти] автовипуск];" тож комірка не звільняється, коли тимчасовий контролер звільнений?
Тод Каннінгем

Гм. Документація, що розповідає про №2, все ще говорить про те, щоб встановити власника комірки у файлі XIB на відомий клас контролера. Можливо, це не має значення, коли ви встановлюєте власника під час завантаження.
Оскар

@OscarGoldman Власник комірки у файлі XIB - це клас (тобто тип власника.) Власник комірки в loadNibNamed: owner: options: є об'єктом типу, визначеного у XIB.
Бентфорд

2
Варіант №2 @CoolDocMan все ще працює. Проблема, швидше за все, з ручкою. Ось приклад: github.com/bentford/NibTableCellExample
bentford

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

це зламає програми iOS5? Я справді ніколи не бачив UINib
Adam Waite

@AdamWaite Реєстрація файлів NIB працює для iOS 5 та новіших версій, тому вона не порушує програми iOS 5. І UINib навіть існує з iOS 4.
Mecki

Хороший приклад, перевірте GIT репозиторій , на який посилається у верхньому відповідь тут: stackoverflow.com/questions/18746929 / ...
netigger

39

Зареєструйтесь

Після iOS 7 цей процес було спрощено до ( швидкий 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"];

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. x вийміть з’єднання від власника файлу
  4. Клацніть правою кнопкою миші на UITableCell в ієрархії об'єктів і повторно додайте з'єднання.

У мене виникла проблема, яку ви згадали, але як вказати на з'єднання до UITableViewCell замість власника файлу? Я не розумію ваших кроків, наприклад, для чого потрібен знімок екрана? і коли я натиснув кнопку "Додати" поруч із розеткою, нічого не відбувається
xu huanze

@xuhuanze Я запропонував зробити знімок екрана, щоб ви мали запис про те, з якими речами вже пов’язаний власник файлу. Тоді ви можете знову створити ті самі з'єднання. Щоб додати з’єднання, потрібно перетягнути й опустити - не лише одним клацанням миші.
funroll

Велике спасибі, у мене була проблема "цей клас не відповідає кодуванню ключових значень для ключа" і вирішив його за допомогою. Я хочу сказати іншим, що ви також повинні змінити клас вашого UITableViewCell на свій клас, який ви використовуєте як власний клас комірок.
Денис Кутлубаєв

14

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

1. Створіть свій Xib в Interface Builder так, як вам подобається

  • Встановіть власника файлу до класу NSObject
  • Додайте UITableViewCell і встановіть його клас MyTableViewCellSubclass - якщо ваш ІБ виходить з ладу (трапляється в Xcode> 4 на момент написання цього запису), просто використовуйте UIView робити інтерфейс у Xcode 4, якщо у вас все ще є прокладка
  • Розмістіть підпогляди всередині цієї комірки та приєднайте свої підключення IBOutlet до свого інтерфейсу в .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;
}

9

Якщо ви використовуєте Інтерфейс Builder для виготовлення комірок, перевірте, чи встановлено Ідентифікатор в інспекторі. Потім переконайтеся, що це те саме, коли викликаєте dequeueReusableCellWithIdentifier.

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


8

Завантаження UITableViewCells від XIB дозволяє економити багато коду, але зазвичай призводить до жахливої ​​швидкості прокрутки (насправді це не XIB, а надмірне використання UIViews, що викликає це).

Я пропоную вам поглянути на це: Посилання на посилання


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 дозволяє лише один об'єкт верхнього рівня - Bug 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;
}

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 (вхід ADC)

стаття cocoawithlove.com Я знайшов цю концепцію (отримайте зразок додатка телефонних номерів)


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


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
    }

}

1

Ось мій метод для цього: Завантаження користувацьких UITableViewCells з файлів XIB ... Ще один метод

Ідея полягає в тому, щоб створити підклас SampleCell UITableViewCellз IBOutlet UIView *contentвластивістю та властивістю для кожного користувальницького підвиду, який потрібно налаштувати з коду. Потім створити файл SampleCell.xib. У цьому файлі nib змініть власника файлу на SampleCell. Додайте вміст UIViewрозміром відповідно до ваших потреб. Додайте та налаштуйте всі потрібні підперегляди (мітка, перегляд зображень, кнопки тощо). Нарешті, зв’яжіть подання вмісту та підзаголовки до власника файлу.


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 розширення конспектує різницю в реєстрації комірок через nib або class.

Приклад використання:

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;

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

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

}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.