В раскадровке, как создать пользовательскую ячейку для использования с несколькими контроллерами?

Я пытаюсь использовать раскадровки в приложении, над которым я работаю. В приложении есть Списки и Пользователи, и каждый из них содержит коллекцию другого (члены списка, списки, принадлежащие пользователю). Итак, у меня есть классы ListCell и UserCell. Цель состоит в том, чтобы их можно было повторно использовать во всем приложении (т.е. В любом из моих контроллеров tableview).

То, где я столкнулся с проблемой.

Как создать пользовательскую ячейку таблицы в раскадровке, которая может быть повторно использована в любом контроллере представления?

Вот конкретные вещи, которые я пробовал до сих пор.

  • В контроллере # 1 добавлена ​​ячейка прототипа, установите класс в мой подкласс UITableViewCell, установите идентификатор повторного использования, добавьте метки и подключите их к выходам классов. В контроллере # 2 добавлена ​​пустая ячейка прототипа, установите ее в тот же класс и повторно используйте идентификатор, как и раньше. Когда он запускается, метки никогда не появляются, когда ячейки отображаются в контроллере №2. Прекрасно работает в контроллере №1.

  • Разработал каждый тип ячейки в другом NIB и подключен к соответствующему классу ячейки. В раскадровке добавлена ​​пустая ячейка прототипа и задана ее идентификатор класса и повторного использования, чтобы ссылаться на мой класс ячеек. В методах viewDidLoad контроллеров зарегистрированы эти файлы NIB для идентификатора повторного использования. Когда показано, ячейки обоих контроллеров были пусты, как прототип.

  • Сохраненные прототипы на обоих контроллерах пустые и задают класс и повторно используют id для моего класса ячейки. Построен пользовательский интерфейс ячеек полностью в коде. Ячейки отлично работают во всех контроллерах.

Во втором случае я подозреваю, что прототип всегда переопределяет NIB, и если я убил ячейки прототипа, то будет работать регистрация моего NIB для повторного использования. Но тогда я бы не смог настроить segues из ячеек на другие фреймы, что на самом деле является целым рядом с использованием раскадровки.

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

Ответ 1

Как я понимаю, вы хотите:

  • Создайте ячейку в IB, которая может использоваться в нескольких сценариях раскадровки.
  • Настройте уникальные раскладки из этой ячейки в зависимости от сцены, в которой находится ячейка.

К сожалению, в настоящее время нет способа сделать это. Чтобы понять, почему ваши предыдущие попытки не сработали, вам нужно больше понять, как работают раскадровки и прототипы. (Если вас не волнует, почему эти другие попытки не сработали, не стесняйтесь уходить сейчас. У меня нет никаких магических обходных решений для вас, кроме того, что вы предлагаете ошибку в файле.)

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

  • Каждая ячейка прототипа на самом деле представляет собой встроенный мини-ниб. Поэтому, когда контроллер табличного представления загружается, он проходит через каждый из прототипов клеточных наконечников и вызывает -[UITableView registerNib:forCellReuseIdentifier:].
  • В представлении таблицы запрашивается контроллер для ячеек.
  • Вы, вероятно, называете -[UITableView dequeueReusableCellWithIdentifier:]
  • Когда вы запрашиваете ячейку с данным идентификатором повторного использования, она проверяет, зарегистрирован ли зарегистрированный nib. Если это так, он создает экземпляр этой ячейки. Это состоит из следующих шагов:

    • Посмотрите на класс ячейки, как определено в ячейке. Вызовите [[CellClass alloc] initWithCoder:].
    • Метод -initWithCoder: проходит и добавляет subviews и устанавливает свойства, которые были определены в nib. (IBOutlet, вероятно, тоже подключился сюда, хотя я не тестировал это, это может произойти в -awakeFromNib)
  • Вы настроите свою ячейку, как хотите.

Важно отметить, что существует различие между классом ячейки и визуальным внешним видом ячейки. Вы могли бы создать две отдельные ячейки прототипа одного и того же класса, но с их подзаголовками, изложенными совершенно по-другому. Фактически, если вы используете стили по умолчанию UITableViewCell, это именно то, что происходит. Например, стиль "Default" и стиль "Subtitle" представлены одним и тем же классом UITableViewCell.

Это важно. класс для ячейки не имеет взаимно однозначной корреляции с конкретной иерархией представлений . Иерархия представлений полностью определяется тем, что в ячейке прототипа, которая была зарегистрирована этим конкретным контроллером.

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


Учитывая эту информацию, давайте посмотрим, что произошло в ваших предыдущих попытках.

В контроллере # 1 добавлена ​​ячейка прототипа, установите класс в мой Подкласс UITableViewCell, установить идентификатор повторного использования, добавить метки и проводные их к классам. В контроллере # 2 добавлен пустой прототипа, установите его в тот же класс и повторно используйте идентификатор, как и раньше. когда он работает, метки никогда не появляются, когда ячейки отображаются в Контроллер №2. Прекрасно работает в контроллере №1.

Ожидается. Хотя обе ячейки имели один и тот же класс, иерархия представлений, которая была передана ячейке в контроллере № 2, была полностью лишена подсмотров. Таким образом, у вас есть пустая ячейка, и именно это вы вложили в прототип.

Разработал каждый тип ячейки в другом NIB и подключен к соответствующий класс ячейки. В раскадровке добавлена ​​пустая ячейка прототипа и установите его идентификатор класса и повторного использования, чтобы ссылаться на мой класс ячеек. В контроллеры viewDidLoad, зарегистрировали эти файлы NIB для повторное использование идентификатора. Когда показано, ячейки обоих контроллеров были пустыми, как прототип.

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

Это решение было близко. Как вы отметили, вы можете просто программно вызвать -[UITableView registerNib:forCellReuseIdentifier:], передав UINib, содержащую ячейку, и вы вернете ту же ячейку. (Это объясняется не тем, что прототип "переопределяет" нить, вы просто не зарегистрировали нить с табличным обзором, поэтому он все еще смотрел на наконечник, встроенный в раскадровку.) К сожалению, есть недостаток в этом подходе - нет никакого способа подключить сериалы раскадровки к ячейке в автономном нобе.

Сохраненные прототипы в обоих контроллерах пустые и заданные идентификаторы класса и повторного использования к классу моей клетки. Построен пользовательский интерфейс ячеек полностью в коде. ячейки отлично работают во всех контроллерах.

Естественно. Надеюсь, это неудивительно.


Итак, почему это не сработало. Вы можете проектировать свои ячейки в автономных перьях и использовать их в нескольких сценариях раскадровки; вы просто не можете в настоящее время подключить раскадровку к этим ячейкам. Надеюсь, что вы кое-чему научились в процессе чтения.

Ответ 2

Несмотря на большой ответ от BJ Homer, я чувствую, что у меня есть решение. Что касается моего тестирования, он работает.

Концепция: создать пользовательский класс для ячейки xib. Там вы можете дождаться события касания и программно выполнить segue. Теперь все, что нам нужно, это ссылка на контроллер, выполняющий Segue. Мое решение - установить его в tableView:cellForRowAtIndexPath:.

Пример

У меня есть DetailedTaskCell.xib, содержащий ячейку таблицы, которую я хотел бы использовать в нескольких табличных представлениях:

DetailedTaskCell.xib

Для этой ячейки существует пользовательский класс TaskGuessTableCell:

enter image description here

Здесь происходит волшебство.

// TaskGuessTableCell.h
#import <Foundation/Foundation.h>

@interface TaskGuessTableCell : UITableViewCell
@property (nonatomic, weak) UIViewController *controller;
@end

// TashGuessTableCell.m
#import "TaskGuessTableCell.h"

@implementation TaskGuessTableCell

@synthesize controller;

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    NSIndexPath *path = [controller.tableView indexPathForCell:self];
    [controller.tableView selectRowAtIndexPath:path animated:NO scrollPosition:UITableViewScrollPositionNone];
    [controller performSegueWithIdentifier:@"FinishedTask" sender:controller];
    [super touchesEnded:touches withEvent:event];
}

@end

У меня есть несколько Segues, но все они имеют одно и то же имя: "FinishedTask". Если вам нужно быть гибким здесь, я предлагаю добавить другое свойство.

ViewController выглядит следующим образом:

// LogbookViewController.m
#import "LogbookViewController.h"
#import "TaskGuessTableCell.h"

@implementation LogbookViewController

- (void)viewDidLoad
{
    [super viewDidLoad]

    // register custom nib
    [self.tableView registerNib:[UINib nibWithNibName:@"DetailedTaskCell" bundle:[NSBundle mainBundle]] forCellReuseIdentifier:@"DetailedTaskCell"];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    TaskGuessTableCell *cell;

    cell = [tableView dequeueReusableCellWithIdentifier:@"DetailedTaskCell"];
    cell.controller = self; // <-- the line that matters
    // if you added the seque property to the cell class, set that one here
    // cell.segue = @"TheSegueYouNeedToTrigger";
    cell.taskTitle.text  = [entry title];
    // set other outlet values etc. ...

    return cell;
}

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if([[segue identifier] isEqualToString:@"FinishedTask"])
    {
        // do what you have to do, as usual
    }

}

@end

Там могут быть более элегантные способы добиться того же, но - он работает!:)

Ответ 3

Я искал это, и я нашел этот ответ Ричардом Вениблем. Это работает для меня.

iOS 5 включает новый метод в UITableView: registerNib: forCellReuseIdentifier:

Чтобы использовать его, поместите UITableViewCell в наконечник. Он должен быть единственным корнем объект в нибе.

Вы можете зарегистрировать nib после загрузки tableView, а затем, когда вы call dequeueReusableCellWithIdentifier: с идентификатором ячейки, он вытащит его из наконечника, как если бы вы использовали раскадровку прототипа.

Ответ 4

BJ Гомер дал отличное объяснение того, что происходит.

С практической точки зрения я бы добавил, что, учитывая, что вы не можете иметь ячейки в качестве xibs и соединить segues, лучший выбор - иметь ячейку в виде xib-переходов, гораздо легче поддерживать, чем компоновки ячеек и свойств в разных местах, и ваши segues, вероятно, будут отличаться от ваших разных контроллеров в любом случае. Вы можете определить segue непосредственно из контроллера табличного представления на следующий контроллер и выполнить его в коде.,

Еще одно замечание: наличие вашей ячейки в виде отдельного xib файла предотвращает возможность подключения любых действий и т.д. непосредственно к контроллеру табличного представления (я так и не отработал), вы не можете определить владельца файла как ничего значимого). Я работаю над этим, определяя протокол, который, как ожидается, будет соответствовать контроллеру вида таблицы ячеек и добавлению контроллера как слабого свойства, аналогичного делегату, в cellForRowAtIndexPath.

Ответ 5

Swift 3

Б. Дж. Гомер дал отличное объяснение. Это помогает мне понять концепцию. Для make a custom cell reusable in storyboard, который можно использовать в любом TableViewController, мы должны приближаться к mix the Storyboard and xib. Предположим, что у нас есть ячейка с именем CustomCell, которая должна использоваться в TableViewControllerOne и TableViewControllerTwo. Я делаю это шагами.
1. Файл > Создать > Выбрать файл > Выбрать Cocoa Класс Touch > нажмите Далее > Дать имя вашего класса (например, CustomCell) > выберите Подкласс как UITableVieCell > Отметьте также файл XIB установите флажок и нажмите "Далее". 2. Настройте ячейку так, как вы хотите, и установите идентификатор в инспекторе атрибутов для ячейки, здесь мы будем устанавливать как CellIdentifier. Этот идентификатор будет использоваться в вашем ViewController для идентификации и повторного использования Cell.
3. Теперь нам нужно register this cell в нашем ViewController viewDidLoad. Нет необходимости в каком-либо методе инициализации.
4. Теперь мы можем использовать эту настраиваемую ячейку в любом tableView.

В TableViewControllerOne

let reuseIdentifier = "CellIdentifier"

override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UINib(nibName: "CustomCell", bundle: nil), forCellReuseIdentifier: reuseIdentifier)
} 

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier:reuseIdentifier, for: indexPath) as! CustomCell
    return cell!
}

Ответ 6

Я нашел способ загрузить ячейку для одного и того же VC, не проверенный для segues. Это может быть обходным путем для создания ячейки в отдельном nib

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

(например: таблица и поле поиска с UISearchController с таблицей результатов, и вы хотите использовать одну и ту же ячейку в обоих)

Когда контроллер запрашивает ячейку, выполните следующее:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString * identifier = @"CELL_ID";

    ContactsCell *cell = [self.YOURTABLEVIEW dequeueReusableCellWithIdentifier:identifier];
  // Ignore the "tableView" argument
}

И здесь у вас есть ваша ячейка из раскадровки