Как подключить табличное представление к контроллеру представления

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

Все это связано с вопросом здесь (вам не нужно смотреть на него - я объясню все ниже): Передать параметр при инициализации таблицы

Я несколько дней работал над одной и той же проблемой, но я понимаю, что должно быть что-то большое, что мне не хватает. Я гуглил и гуглил, и я даже купил (и около 50% прочитали) две книги Obj-C несколько дней назад, но я все еще цепляюсь за то, что кажется невероятно простым. Очевидно, я новичок в ООП, но у меня посредственные навыки в HTML, Perl, SQL, Python, а также в некоторых древних вещах, таких как Pascal и Basic. Я n00b, но не придурок (ну вообще-то этот опыт немного меняет мое мнение).

В любом случае, моя конечная цель здесь — просто создать приложение с 8 «обычными» кнопками в первом представлении (уровень 1), каждая из которых в основном делает одно и то же — показывать простое табличное представление (уровень 2) с данными в ячейки, которые можно щелкнуть, чтобы продолжить детализацию (уровень 3). Очень простая и простая концепция. Единственная разница между 8 возможными уровнями 2 заключается в отображаемых данных. Я уже создал SQL-запросы, которые работают так, как я хочу для каждой кнопки.

Итак, вот моя точка зрения: у меня есть прекрасно работающее приложение, которое делает все, начиная с уровня 2, точно так, как я ожидаю — запросы работают, таблицы прекрасны — так что это здорово.

Кроме того, у меня есть еще одно навигационное приложение, которое запускается на «уровне 1» и показывает мне 8 кнопок (я скрываю панель навигации на уровне 1). Если я нажму любую из кнопок на уровне 1, вид уровня 2 (который представляет собой панель навигации + таблица) появится в представлении точно так, как я хочу. Проблема в том, что таблица пуста. Что бы я ни делал, я не могу получить уровень 2 во втором приложении, чтобы показать мне данные, хотя я могу отлично показать все эти данные в первом приложении. На всю жизнь я не могу понять, как «связать» уровень 1 с уровнем 2.

Надеюсь, вы понимаете этот пробел, который я пытаюсь преодолеть. Поскольку существует 8 возможностей для уровня 2 (с очень небольшими различиями в запросах sql для одной и той же таблицы sql), я сначала попытался придумать способ «передачи» целого числа в представление уровня 2 (в первом приложении) и затем выбор SQL-запроса на основе того, что было передано (см. ссылку выше для этого фиаско). Как только у меня это заработало, я планировал позже выяснить, как заставить кнопки выполнять «прохождение». Однако после примерно 16 часов возни с этим я просто сдался и решил сделать 8 разных контроллеров табличного представления, все с почти идентичным кодом, кроме запроса. Таким образом, если бы я мог просто получить ОДНУ кнопку на уровне 1, чтобы просто нажать только на ОДНУ кнопку уровня 2 без параметров, я был бы ужасным, но успешным программистом.

К сожалению, даже это не сработало для меня. Я перепробовал все возможные комбинации управления-перетаскивания и окна/представления/таблицы, какие только мог придумать в Interface Builder, но что бы я ни пытался, данные никогда не загружались в табличное представление, хотя в моем первом приложении это прекрасно работает. Я просмотрел каждую строку кода - они одинаковы, за исключением того, что что-то должно «вызвать» или «запустить» часть уровня 2, и я просто не понимаю.

Итак, я собираюсь порвать с соглашением/ожиданиями и не публиковать код в своем вопросе. Я просто хочу знать - как это может быть так сложно?? У меня очень аналитический склад ума, и я быстро схватываю, но я должен сказать, что никогда в жизни не был так смирен техническими проблемами.

Может ли кто-нибудь объяснить мне на концептуальном уровне, что мне нужно здесь делать или что мне не хватает? Даже если вы дадите мне ссылку на что-нибудь почитать, я был бы очень признателен. Я просмотрел десятки часов уроков на YouTube, но я всегда готов к большему.

Конечно, я готов поделиться своим кодом, но его так много, и я настолько новичок в этом, что действительно не знаю, где находятся соответствующие части. Кроме того, я действительно хочу узнать, как все это работает, чтобы я мог помочь другим. Если здесь есть такая вещь, как PM, я отправлю ее вам по электронной почте, если вы готовы взглянуть. Как только я заработаю, я опубликую код здесь. Я должен верить, что есть другие люди, которые ищут то же самое, что и я. Однако, что более важно, я просто хочу знать на высоком уровне, как правильно подойти к моей проблеме? Если вы посмотрите на мою ссылку, вы увидите, что я пытался (то есть передать целое число методу, заполняющему таблицу), но, как я уже сказал, я в основном отказался от этого, потому что я ничего не добился. Люди пытаются мне помочь, но я идиот.

Спасибо, что выдержали мое мучительно длинное сообщение. Если вы зашли так далеко и у вас есть какие-то предложения для меня, я весь слушаю. Но я буду честен - если вы скажете мне, что я должен просто отказаться от всего этого и использовать основные данные, я заплачу. Я действительно не думаю, что у меня есть время, чтобы придумать совершенно другой способ управления данными. Как я уже сказал, я очень доволен базой данных и частями запросов моего приложения - меня убивает только управление чертовыми представлениями и передача данных между ними!

Любая помощь приветствуется - всем большое спасибо.


person truthsmiles    schedule 06.06.2011    source источник
comment
Почему минус? Вопрос совершенно законный, а не просто еще один вопрос домашнего задания.   -  person JustSid    schedule 06.06.2011
comment
@truthsmiles - я буду рад объяснить вам это, не стесняйтесь пинговать меня в чате Google. (moshberm) А пока попробую написать ответ.   -  person Moshe    schedule 07.06.2011
comment
@ Моше, -1, если бы я мог, то, что я говорю кому-то обсудить это в частном порядке, противоречит всей модели SO, потому что это никогда никому не поможет.   -  person Pops    schedule 07.06.2011
comment
@правда, я не минусовал тебя, но некоторые люди здесь могут стать... колючими... когда ты называешь сайт форумом. Это потому, что SO был запущен для решения многих проблем, с которыми сталкиваются форумы.   -  person Pops    schedule 07.06.2011
comment
@LordTorgamus - Милорд, если мой ответ вам нравится, вы можете проголосовать. ;-)   -  person Moshe    schedule 07.06.2011
comment
@ Моше, думаю, все в порядке, но я ничего не знаю об Obj-C, поэтому не буду голосовать ни за одно из направлений. Я только что пришел к этому вопросу из-за случайной ссылки, которую вы разместили.   -  person Pops    schedule 07.06.2011


Ответы (1)


Если я правильно понимаю ваш вопрос, вы спрашиваете, как инициализировать контроллер представления и передать некоторые данные, чтобы изменить его поведение. Ключевая концепция здесь, чтобы понять, как объекты инициализируются в Objective-C. Один из самых распространенных вопросов, которые возникают у разработчиков, которые плохо знакомы с iOS:

Как может I pass data между моими представления?

Да, там восемь разных ссылок. (Хорошо, эта восьмая ссылка немного не по теме, но она достаточно близка.) Есть несколько способов сделать это, и я кратко расскажу о них. Я также опишу пользовательские инициализаторы, которые также важны.

Давайте представим, что мы создаем приложение-каталог, которое показывает кучу товаров в разных категориях. Представьте, что наше приложение открывается со списком продуктов, как в приложении Apple Store. Скажем, когда пользователь нажимает на продукт, мы хотим показать страницу продукта.

  1. Вы можете установить свойства в «следующем» контроллере представления. — Просто мы можем создать подкласс UIViewController и установить свойство productID (которое мы придумали). Давайте назовем наш новый UIViewController ProductPageViewController. Вот как это будет выглядеть:

    - (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
    
      //Create a ProductPageViewController
      ProductPageViewController *ppvc = [[ProductPageViewController alloc] initWithNibName:@"ProductPageViewController" bundle:nil];
      //set the property on our ProductPageViewController
      [ppvc setProductID:42];
      //We would usually present the PPVC here.
      //After presenting, remember to release the view controller
    }
    

    В первой строке мы создаем контроллер представления продукта. Звоним alloc, потом init. (Функции завернуты — то есть мы вызываем init непосредственно на результат метода alloc.)

    Затем мы устанавливаем свойство нашего представления. Теперь вид можно настроить в viewWillAppear, и все в порядке.

  2. Вы можете обмениваться данными через постоянное хранилище. Этот метод работает немного по-другому. Контроллеры представления вообще не взаимодействуют, за исключением того, что первый представляет второй. Всякий раз, когда значение в первом представлении изменяется (которое вы хотите сохранить), вы записываете его в Core Data или NSUserDefaults. Затем новое представление считывает значение по мере необходимости.

    В вашем первом контроллере представления:

    //A method to store the data
    - (void)storeData:(id)pageID{
        [[NSUserDefaults setObject:pageID forKey:@"pageID"];
    }
    
    - (void)showNewPPVC{
    
      ProductPageViewController *ppvc = [[ProductPageViewController alloc] initWithNibName:@"ProductPageViewController" bundle:nil];        
      //Show and then release the PPVC
    }
    
  3. Вы можете использовать пользовательские инициализаторы. Это, вероятно, наиболее интуитивно понятный способ сделать это, если вы понимаете концепцию, потому что это единственный способ, при котором данные фактически "передаются". (В отличие от метода 2, в котором данные не передаются напрямую, и метода 1, в котором данные передаются как свойство.)

    Обратите внимание, что в предыдущих примерах я использовал метод initWithNibName:Bundle. Вы также можете заметить, что UITableViewControllers используют другой инициализатор, initWithStyle:. Эти два инициализатора принимают некоторую информацию для нового объекта, чтобы он знал, как его загрузить. Сначала рассмотрим первый:

    - (id)initWithNibName:(NSString *)nibNameOrNil Bundle:(NSBundle *)bundleNameOrNil;
    

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

    - (id)initWithStyle:(UITableViewStyle)style;
    

    Здесь вы можете передать одно из двух значений UITableViewStyle. Это один из способов определить стиль табличного представления (другой способ — напрямую изменить файл пера).

    Давайте немного расширим эту концепцию на наш пример. Сейчас я покажу вам, как сделать свой собственный инициализатор. Давайте инициализируем наш экземпляр ProductPageViewController:

    - (id) initWithProductID:(int)productID;
    

    Это достаточно просто. Теперь нам нужно реализовать метод и что-то сделать с идентификатором продукта. Мы начнем с базового кода, необходимого для «имитации» функциональности инициализатора по умолчанию.

    - (id) initWithProductID:(int)productID{
     self = [super init];
    
     return self;
    }
    

    Этот метод вернет инициализированную копию нашего ProductPageViewController, однако он еще не загрузит наш пользовательский интерфейс из NIB, или, если бы это был UITableViewController, он не установил бы UITableViewStyle. Давайте сначала поработаем с NIB, а потом я покажу, как работать с UITableViewController. Так...

    - (id) initWithProductID:(int)productID{
     self = [super initWithNibName:@"ProductPageViewController" Bundle:nil];
    
     return self;
    }
    

    В настоящее время. у нас есть инициализированный ProductPageViewController, загруженный из NIB, но он еще ничего не делает. Обратите внимание, что мы не раскрываем аргументы NibName и Bundle, а просто передаем их сами. Если вы хотите, вы можете теоретически разоблачить и их. Теперь давайте возьмем этот productID и сделаем с ним что-нибудь.

    - (id) initWithProductID:(int)productID{
     self = [super initWithNibName:@"ProductPageViewController" Bundle:nil];
    
       if(self){
         self.prodID = productID;
       }
    
     return self;
    }
    

    С нашими последними изменениями наш "PPVC" теперь знает о productID. Он может запрашивать базу данных, как вы хотите, и делать что-то с результатами. Затем вы можете запускать различные запросы на основе этого productID.

Еще два простых совета:

  1. Возможно, вы хотите передать несколько аргументов. Конечно, вы можете просто добавить к ним сигнатуру метода - (id) initWithProductID:(int)productID andCategoryID(int)categoryID, но что произойдет, если у вас будет пять, шесть или пятьдесят шесть (да, это очень много) аргументов? Я бы посоветовал передать коллекцию или массив аргументов.

  2. Чтобы использовать пользовательские инициализаторы с UITableView, вы передаете UITableViewStyle вместо имени NIB. Вот как это может выглядеть:

          - (id) initWithProductID:(int)productID{
            self = [super initWithStyle:UITableViewStyleGrouped];
    
            if(self){
              self.prodID = productID;
            }
    
            return self;
          }
    

При создании ваших подразделов я бы предложил комбинацию постоянных данных и пользовательских инициализаторов. Также советую взглянуть на методы viewDidLoad и viewWillAppear.

person Moshe    schedule 06.06.2011