Як зробити в раскадровці спеціальну комірку для використання з декількома контролерами?


216

Я намагаюся використовувати розкадровки в додатку, над яким працюю. У програмі є Списки та Користувачі, і кожен містить колекцію інших (члени списку, списки, що належать користувачеві). Тож, відповідно, у мене є ListCellі UserCellкласи. Мета полягає в тому, щоб вони могли бути повторно використані у всій програмі (тобто в будь-якому з моїх контролерів tableview).

Тут я стикаюся з проблемою.

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

Ось конкретні речі, які я намагався досі.

  • У Controller # 1 додав комірку прототипу, встановив клас для мого UITableViewCellпідкласу, встановив ідентифікатор повторного використання, додав мітки та підвів їх до розеток класу. У контролері №2 додано порожню комірку прототипу, встановлено його в той самий клас і повторно використаний ідентифікатор, як і раніше. Під час запуску мітки ніколи не з’являються, коли комірки відображаються в контролері №2. Прекрасно працює в контролері №1.

  • Розроблено кожен тип комірок в іншому NIB і підключено до відповідного класу комірок. У раскадровці додано порожню комірку прототипу та встановлено її клас та повторне використання ідентифікатора для посилання на мій клас комірки. У viewDidLoadметодах контролерів реєстрували ці файли NIB для ідентифікатора повторного використання. Коли показано, комірки в обох контролерах були порожніми, як у прототипу.

  • Зберігав прототипи в обох контролерах, порожній і встановив клас, і повторно використовував ідентифікатор для мого класу комірки. Побудований користувальницький інтерфейс комірок повністю в коді. Клітини чудово працюють у всіх контролерах.

У другому випадку я підозрюю, що прототип завжди перевизначає NIB, і якби я вбив комірки прототипу, реєстрація мого NIB для ідентифікатора повторного використання могла б спрацювати. Але тоді я не зміг би налаштувати сегменти з клітинок на інші кадри, що насправді і полягає у всьому сенсі використання розкадрувань.

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

Відповіді:


205

Як я розумію, ви хочете:

  1. Створіть комірку в IB, яку можна використовувати в декількох сценах розкадрування.
  2. Налаштуйте унікальні розділи розкадровки з цієї комірки, залежно від сцени, в якій знаходиться камера.

На жаль, наразі цього неможливо зробити. Щоб зрозуміти, чому ваші попередні спроби не працювали, вам слід більше зрозуміти, як працюють розкадровки та прототипи комірок табличного подання. (Якщо вам байдуже, чому ці інші спроби не спрацювали, сміливо йдіть зараз. У мене немає для вас магічних обхідних шляхів, крім того, щоб запропонувати вам подати помилку.)

По суті, розкадровка - це не набагато більше, ніж колекція файлів .xib. Коли ви завантажуєте контролер подання таблиці, який має кілька комірок прототипу з розкадрування, ось що відбувається:

  • Кожна комірка-прототип насправді є власним вбудованим міні-пером. Отже, коли контролер подання таблиці завантажується, він проходить через кожну з перил та викликів комірки прототипу -[UITableView registerNib:forCellReuseIdentifier:].
  • Вигляд таблиці запитує контролер для комірок.
  • Ви, мабуть, телефонуєте -[UITableView dequeueReusableCellWithIdentifier:]
  • Коли ви запитуєте комірку із заданим ідентифікатором повторного використання, вона перевіряє, чи зареєстровано перо. Якщо це так, він створює екземпляр екземпляра цієї комірки. Це складається з наступних кроків:

    1. Подивіться на клас клітини, як визначено в пероці клітини. Телефонуйте [[CellClass alloc] initWithCoder:].
    2. -initWithCoder:Метод проходить і додає подпанелі і набори властивостей , які були визначені в бобах. ( IBOutletможливо, і тут зачепиться, хоча я цього не перевіряв; це може статися в -awakeFromNib)
  • Ви налаштовуєте клітинку як завгодно.

Тут важливо відзначити, що існує різниця між класом клітини та візуальним виглядом клітини. Ви можете створити дві окремі комірки прототипу одного класу, але з їхніми поданнями, викладеними абсолютно по-різному. Насправді, якщо ви використовуєте UITableViewCellстилі за замовчуванням , це саме те, що відбувається. Наприклад, стиль "За замовчуванням" і "Субтитри" представлені одним UITableViewCellкласом.

Це важливо : Клас комірки не має індивідуальної кореляції з певною ієрархією подання . Ієрархія подання повністю визначається тим, що знаходиться в комірці прототипу, яка була зареєстрована у цьому конкретному контролері.

Також зауважте, що ідентифікатор повторного використання клітинки не був зареєстрований у якомусь глобальному диспансері клітин. Ідентифікатор повторного використання використовується лише в контексті одного UITableViewекземпляра.


Беручи до уваги цю інформацію, давайте розглянемо, що сталося у ваших вищезазначених спробах.

У Controller # 1 додав комірку прототипу, встановив клас для мого підкласу UITableViewCell, встановив ідентифікатор повторного використання, додав мітки та підвів їх до розеток класу. У контролері №2 додано порожню комірку прототипу, встановлено його в той самий клас і повторно використаний ідентифікатор, як і раніше. Під час запуску мітки ніколи не з’являються, коли комірки відображаються в контролері №2. Прекрасно працює в контролері №1.

Це очікується. Хоча обидві комірки мали один і той самий клас, ієрархія подання, яка передавалась комірці в контролері №2, була повністю позбавлена ​​підпрозорів. Отже, ви отримали порожню клітинку, що саме те, що ви помістили в прототип.

Розроблено кожен тип комірок в іншому NIB і підключено до відповідного класу комірок. У раскадровці додано порожню комірку прототипу та встановлено її клас та повторне використання ідентифікатора для посилання на мій клас комірки. У методах viewDidLoad контролерів зареєстровано ці файли NIB для ідентифікатора повторного використання. Коли показано, комірки в обох контролерах були порожніми, як у прототипу.

Знову ж цього очікуємо. Ідентифікатор повторного використання не використовується спільно між сценаріями розкадрувань або перонами, тому той факт, що всі ці окремі клітинки мали однаковий ідентифікатор повторного використання, не мав сенсу. Клітинка, яку ви повернете з табличного подання, матиме вигляд, який відповідає комірці прототипу в цій сцені розкадрування.

Однак це рішення було близьким. Як ви вже зазначали, ви можете просто програмно зателефонувати -[UITableView registerNib:forCellReuseIdentifier:], передавши UINibміститься комірку, і отримаєте ту саму комірку. (Це не тому, що прототип «перекривав» перо; ви просто не зареєстрували перо в режимі таблиці, тому він все ще дивився на перо, вбудоване в розкадрування.) На жаль, такий підхід має недолік - немає можливості підключити сюжети розкадровки до клітини в автономному перо.

Зберігав прототипи в обох контролерах, порожній і встановив клас, і повторно використовував ідентифікатор для мого класу комірки. Побудований користувальницький інтерфейс комірок повністю в коді. Клітини чудово працюють у всіх контролерах.

Природно. Сподіваємось, це не дивно.


Отже, тому це не спрацювало. Ви можете проектувати свої клітини в автономних перочках і використовувати їх у кількох сценах розкадровки; ви просто не можете в даний час підключити до цих клітин розділи розкадровки. Сподіваємось, однак, ви щось дізналися в процесі читання цього.


Ах, я розумію. Ви зачепили моє непорозуміння - ієрархія поглядів повністю не залежить від мого класу. Очевидно в ретроспективі! Дякую за чудову відповідь.
Cliff W

Здається, це вже неможливо: stackoverflow.com/questions/8574188/…
Rich Apodaca

7
@RichApodaca Я згадав це рішення у своїй відповіді. Але це не в розкадруванні; це в окремому перо. Таким чином, ви не можете підключити сеги чи робити інші речі, пов’язані з розкадровкою. Отже, це не повністю стосується початкового питання.
BJ Homer

Починаючи з XCode8, такий спосіб вирішення проблем, здається, спрацьовує, якщо вам потрібне рішення лише для розкадрування. Крок 1) Створіть свою прототипну комірку в поданні таблиці в ViewController # 1 і зв’яжіть із користувацьким класом UITableViewCell. З часом вам доведеться пам’ятати про розповсюдження оновлень копій комірки вручну, видаливши копії, зроблені вами в раскадровці, та вставивши назад в оновлений прототип.
jengelsma

58

Незважаючи на чудову відповідь Б. Дж. Гомера, я відчуваю, що маю рішення. Що стосується мого тестування, воно працює.

Концепція: Створіть власний клас для комірки xib. Там ви можете почекати сенсорної події і виконати програму програмно. Тепер нам потрібно лише посилання на контролера, який виконує Segue. Моє рішення - встановити tableView:cellForRowAtIndexPath:.

Приклад

У мене є DetailedTaskCell.xibкомірка таблиці, яку я хотів би використовувати в декількох поданнях таблиці:

DetailedTaskCell.xib

Для TaskGuessTableCellцієї комірки існує власний клас :

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

Тут відбувається магія.

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

У мене є кілька перетікає , але всі вони мають однакову назву: "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

Можуть бути більш елегантні способи досягти того ж, але - це працює! :)


1
Дякую, я застосовую цей підхід у своєму проекті. Ви можете замінити цей метод, щоб вам не потрібно було отримувати indexPath і самостійно вибирати рядок: - (void) setSelected: (BOOL) вибраний анімований: (BOOL) анімований {[super setSelected: selected animated: animated]; якщо (вибрано) [self.controller performSegueWithIdentifier: self.segue sender: self]; } Я думав, що супер вибере клітинку при виклику [super touchesEnded: touches withEvent: event] ;. Чи знаєте ви, коли він вибраний, якщо його немає?
thejaz

9
Зверніть увагу, що за допомогою цього рішення ви запускаєте сегу кожен раз, коли дотик закінчується всередині клітини. Сюди входить, якщо ви просто прокручуєте клітинку, а не намагаєтесь її виділити. Можливо, вам пощастить перевизначити -setSelected:клітинку і викликати сегу лише при переході з NOна YES.
BJ Homer

Мені більше пощастило setSelected:, BJ. Дякую. Дійсно, це неелегантне рішення (воно відчувається неправильно), але в той же час воно працює, тому я використовую його, поки це не виправиться (або щось не зміниться в суді Apple).
Ben Kreeger

16

Я шукав це і знайшов цю відповідь Річарда Венабле. Це працює для мене.

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

Щоб використовувати його, покладіть UITableViewCell у перо. Це повинен бути єдиний кореневий об’єкт у перо.

Ви можете зареєструвати перо після завантаження tableView, тоді, коли ви викликаєте dequeueReusableCellWithIdentifier: за допомогою ідентифікатора клітинки він витягує його з наконечника, подібно до того, якби ви використовували прототип комірки Storyboard.


10

Б. Дж. Гомер дав чудове пояснення того, що відбувається.

З практичної точки зору я б додав, що, враховуючи те, що ви не можете мати комірки як xibs І з'єднувати сегменти, найкращим з них буде вибрати клітинку як xib - переходи набагато простіші у підтримці, ніж макети комірок і властивості в кількох місцях , і ваші сегменти, імовірно, будуть відрізнятися від різних контролерів у будь-якому випадку. Ви можете визначити сегмент безпосередньо з контролера подання таблиці на наступний контролер і виконати його в коді. .

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


10

Стрімкий 3

Б. Дж. Гомер дав чудове пояснення, це допомагає мені зрозуміти цю концепцію. До make a custom cell reusable in storyboard, який може бути використаний у будь-якому TableViewController, до якого ми маємо mix the Storyboard and xibпідійти. Припустимо, у нас є комірка з іменем as, CustomCellяка буде використана в TableViewControllerOneі TableViewControllerTwo. Я роблю це кроками.
1. Файл> Створити> Клацніть Файл> Виберіть клас какао-сенсорного> натисніть Далі> Вкажіть назву свого класу (наприклад CustomCell)> виберіть Підклас як UITableVieCell> Поставте прапорець біля пункту також створити файл XIB і натисніть Далі.
2. Налаштуйте комірку як завгодно і встановіть ідентифікатор в інспекторі атрибутів для комірки, тут ми встановимо як CellIdentifier. Цей ідентифікатор буде використаний у вашому ViewController для ідентифікації та повторного використання комірки.
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!
}

5

Я знайшов спосіб завантажити осередок для того самого ВК, не перевірений для сегментів. Це може бути обхідним шляхом для створення комірки в окремому перо

Скажімо, у вас є один 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
}

І ось у вас є ваша клітина з розкадрування


Я спробував це, і, здається, це працює, Втім, клітини ніколи не використовуються повторно. Система завжди створює та розблоковує нові клітини кожного разу.
GilroyKilroy

Це та сама рекомендація, яку можна знайти в розділі Додавання рядка пошуку до подання таблиці за допомогою розкадрувань . Якщо вам цікаво, там є більш глибоке пояснення цього рішення (пошук tableView:cellForRowAtIndexPath:).
Почутливий

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