Подтвердить что ты не робот

Проблема UITableView при использовании отдельного делегата/источника данных

Общее описание:

Чтобы начать с того, что работает, у меня есть UITableView, который был помещен в Xcode-сгенерированное представление с помощью Interface Builder. Владелец файла вида настроен на подкласс, созданный Xcode, UIViewController. В этот подкласс я добавил рабочие реализации numberOfSectionsInTableView: tableView:numberOfRowsInSection: и tableView:cellForRowAtIndexPath:, а Table View dataSource и delegate подключены к этому классу через File Owner в Interface Builder.

Вышеупомянутая конфигурация работает без проблем. Проблема возникает, когда я хочу переместить этот вид таблицы dataSource и delegate -изменения в отдельный класс, скорее всего, потому что в представлении помимо представления таблицы есть другие элементы управления, и я бы хотел переместить представление таблицы связанный код с его собственным классом. Для этого попробуйте следующее:

  • Создайте новый подкласс UITableViewController в Xcode
  • Переместите известные хорошие реализации numberOfSectionsInTableView:, tableView:numberOfRowsInSection: и tableView:cellForRowAtIndexPath: в новый подкласс
  • Перетащите UITableViewController на верхний уровень существующего XIB в InterfaceBuilder, удалите UIView/UITableView, которые автоматически создаются для этого UITableViewController, затем установите класс UITableViewController в соответствие с новым подклассом
  • Удалите ранее работающие UITableView существующие dataSource и delegate соединения и подключите их к новому UITableViewController

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

  • Когда загружается UITableView, я получаю ошибку времени выполнения, указывающую, что я отправляю tableView:cellForRowAtIndexPath: объекту, который его не распознает.
  • Когда загружается UITableView, проект разбивается на отладчик без ошибок
  • Ошибка отсутствует, но UITableView не отображается

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

Полные шаги для воспроизведения:

  • Создайте новую iPhone OS, приложение на основе View в Xcode и назовите ее TableTest
  • Откройте TableTestViewController.xib в Interface Builder и перетащите UITableView на предоставленную поверхность.
  • Подключите UITableView dataSource и delegate -outlets к файловому владельцу, который уже должен представлять класс TableTestViewController. Сохраните изменения.
  • Вернитесь в Xcode, добавьте следующий код в TableTestViewController.m:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    NSLog(@"Returning num sections");
    return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    NSLog(@"Returning num rows");
    return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    NSLog(@"Trying to return cell");
    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
    }
    cell.text = @"Hello";
    NSLog(@"Returning cell");
    return cell;
}
  • Build и Go, и вы должны увидеть слово Hello в UITableView

  • Теперь, чтобы попытаться переместить эту логику UITableView в отдельный класс, сначала создайте новый файл в Xcode, выбрав подкласс UITableViewController и вызовите класс TableTestTableViewController

  • Удалите приведенный фрагмент кода из TableTestViewController.m и поместите его в TableTestTableViewController.m, заменив стандартную реализацию этих трех методов на наш.
  • В интерфейсе Builder в том же файле TableTestViewController.xib перетащите UITableViewController в основное окно IB и удалите новый UITableView объект, который автоматически появился с ним
  • Установите класс для этого нового UITableViewController в TableTestTableViewController
  • Удалите привязки dataSource и delegate из существующего ранее работающего UITableView и повторно подключите те же самые два привязки к новому TableTestTableViewController, который мы создали.
  • Сохраняйте изменения, стройте и идите, и если вы получаете результаты, которые я получаю, обратите внимание, что UITableView больше не функционирует должным образом

Решение: Еще несколько проблем с устранением неполадок и некоторая помощь от Форумы разработчиков iPhone, я документировал решение! Для основного подкласса UIViewController проекта требуется выход, указывающий на экземпляр UITableViewController. Для этого просто добавьте следующее в заголовок основного представления (TableTestViewController.h):

#import "TableTestTableViewController.h"

и

IBOutlet TableTestTableViewController *myTableViewController;

Затем в интерфейсе Builder подключите новую розетку от File Owner к TableTestTableViewController в главном окне IB. Никаких изменений не требуется в части пользовательского интерфейса XIB. Просто имея эту розетку на месте, даже если код пользователя не использует ее напрямую, она полностью устраняет проблему. Благодаря тем, кто помог, и кредитует BaldEagle на форумах разработчиков iPhone для поиска решения.

4b9b3361

Ответ 1

Я последовал твоим шагам, воссоздал проект и столкнулся с той же проблемой. В основном ты почти там. Осталось 2 вещи (после их исправления):

  • Вам нужно подключить tableView к TableTestTableViewController к UITableView, который у вас есть на экране. Как я уже сказал, потому что это не IBOutlet, вы можете переопределить свойство tableView и сделать его и IBOutlet:

    @interface TableTestTableViewController : UITableViewController {
        UITableView *tableView;
    }
    
    @property (nonatomic, retain) IBOutlet UITableView *tableView;
    
  • Следующее - добавить ссылку на TableTestTableViewController и сохранить ее в TableTestViewController. В противном случае ваш TableTestTableViewController может быть выпущен (после того, как загрузите нить ничем, чтобы на него нависать). Вот почему вы видите неустойчивые результаты, сбои или ничего не показываете. Для этого добавьте:

    @interface TableTestViewController : UIViewController {
        TableTestTableViewController *tableViewController;
    }
    
    @property (nonatomic, retain) IBOutlet TableTestTableViewController  *tableViewController;
    

    и подключите это в построителе интерфейса к экземпляру TableTestTableViewController.

С приведенным выше это отлично работало на моей машине.

Также я думаю, что было бы полезно указать мотивацию всего этого (вместо того, чтобы просто использовать UITableViewController со своим собственным UITableView). В моем случае это было использование других представлений, что только UITableView на одном экране содержимого. Поэтому я могу добавить другие UILabels или UIImages под UIView и показать UITableView под ними или над ними.

Ответ 2

Я просто провел много часов, вытаскивая свои волосы, пытаясь понять, почему UITableView не появлялся, когда я его вложил в отдельный наконечник, а не в главный наконечник. Я наконец нашел ваше обсуждение выше и понял, что это потому, что мой UITableViewController не сохранился! По-видимому, свойства делегата и datasource UITableView не отмечены как "сохранить", и поэтому мой nib загружается, но контроллер был взят... И из-за чудеса objective-c у меня вообще не появилось сообщений об ошибках... Я до сих пор не понимаю, почему это не сбой. Я знаю, что я видел сообщение, отправленное выпущенному xxx, прежде чем... почему он не дал мне одну из этих?!?

Я думаю, что большинство разработчиков предположили, что структура, которую они создают в построителе интерфейса, будет храниться в каком-то более крупном контексте (Nib) и не подлежит освобождению. Наверное, я знаю, почему они это делают. Так что iPhone может упасть и перезагрузить части ниба на низкой памяти. Но человеку это трудно понять.

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

Также - о подключении представления. Во-первых, если вы втащите его из конструктора пользовательского интерфейса, вы увидите, что они подключают свойство представления (которое является IBOutlet) к представлению таблицы. Не нужно выставлять tableView, который, кажется, устанавливается внутри. На самом деле даже нет необходимости устанавливать представление, если вы не хотите уведомления viewDidLoad. Я только что нарушил соединение вида между UITableView и UITableViewController (только делегирование и набор данных), и, похоже, он работает нормально.

Ответ 3

Да почему-то (пожалуйста, звоните, если кто-нибудь знает, почему...) tableView Свойство UITableViewController не отображается как IBOutlet, хотя оно является общедоступным. Поэтому, когда вы используете Interface Builder, вы не можете видеть это свойство для подключения к вашему другому UITableView. Поэтому в вашем подклассе вы можете создать свойство tableView, помеченное как IBOutlet, и подключить его.

Все это кажется взломанным и обходным путем для меня, но это единственный способ разделить UITableViewController's UITableView и поместить его где-то еще в иерархию пользовательского интерфейса. Я столкнулся с той же проблемой, когда пытался создать представление, где есть вещи, отличные от UITableView, и именно так я решил это... Правильно ли это подходит?

Ответ 4

Я смог выполнить эту работу. Я построил действительно красивую панель инструментов с 4 TableViews и webview с видео. Ключ состоит из отдельных контроллеров tableView и IBOutlets для других контроллеров tableview, определенных в контроллере представления. В UIB вам просто нужно подключить другие контроллеры tableview к владельцу файла контроллера вида. Затем подключите таблицы к соответствующим контроллерам представления для источника данных и делегата.