Могут ли несколько контейнерных сцен использовать общий контроллер представления?

У меня есть четыре сцены в моей раскадровке. Одна сцена действует как родитель для всех остальных через представления контейнера. Все устроено так:

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

Если вы прищуритесь, вы заметите, что все четыре являются подклассами одного и того же контроллера представления. Я сделал это, чтобы можно было подключить элементы каждой сцены к одному общему контроллеру представления и избежать четырехкратного подкласса UIViewController. Реализация ProductDetailViewController выглядит так:

@implementation ProductDetailViewController {
    // Scene 1
    __weak IBOutlet UINavigationBar *_navigationBar;

    // Scene 2
    __weak IBOutlet UILabel *_productName;

    // Scene 3
    __weak IBOutlet UILabel *_typeNameLabel;
    __weak IBOutlet UILabel *_categoryNameLabel;
    __weak IBOutlet UIImageView *_richImage;

    // Scene 4
    __weak IBOutlet UIImageView *_productImageView;
}

Проблема в том, что viewDidLoad срабатывает четыре раза (очевидно), и все отображается пустым. Когда я прохожу через отладчик, объект продукта, который я передаю, равен nil в течение трех циклов, а затем инициализируется на четвертом. Может быть, контроллеры представления загружаются не по порядку?

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


person jmoneystl    schedule 22.01.2015    source источник
comment
Из вашего изображения и описания совсем не ясно, почему вам нужны 3 контроллера представления, а не 3 представления, которые все были бы подвидами вашего одного контроллера. Что вы хотите сделать в этих 3-х дочерних контроллерах?   -  person rdelmar    schedule 22.01.2015


Ответы (2)


Установка нескольких .xibs для одного и того же класса является распространенным явлением. Нежелание создавать подклассы UIViewController четыре раза также является распространенным явлением.

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

Эту структуру можно спасти, и она может многое порекомендовать, но для этого потребуются определенные меры. Хороший способ - чтобы родитель-делегат принимал все решения.

  1. Дайте классу свойство делегата, своего рода, конечно, слабое (слабое, потому что дочерний элемент не может поддерживать жизнь родителя). На протяжении всего этого вы можете проверить, является ли он nil, потому что, если у него есть делегат, он является дочерним, но если у него нет делегата, это родитель. Впрочем, обычно вам и не нужно знать.

  2. Используйте идентификаторы перехода. В IB присвойте каждому из этих переходов идентификатор. Затем в своем классе View Controller реализуйте prepareForSegue:sender: с телом

    if ([segue.identifier isEqualToString:@"ProductNameIdentiferInStoryboard"]) {
        /*...*/ } 
    else if ([segue.identifier isEqualToString:@"typeBarIdentiferInStoryboard"]) { 
        /*...*/  } 
    else if ([segue.identifier isEqualToString:@"productImageIdentifierInStoryboard"]) {  /*...*/ }
    
  3. Реализуйте три свойства, опять же одного типа, каждое из которых представляет childViewController,

    @property ProductDetailViewController *productNameViewController; 
    @property ProductDetailViewController *productTypeBarViewController;
    @property ProductDetailViewController *productImageBarViewController;`
    
  4. В prepareForSegue из шага 2 заполните шаблоном:

    self.productNameViewController = segue.destinationViewController;
    self.productNameViewController.delegate = self;
    
  5. Обратите внимание, что prepareForSeque:sender: срабатывает перед любым viewDidLoad, поэтому у вас есть ссылки: выполните все настройки здесь с точки зрения родителя. self.productNameViewController.titleLabel.text = @"Product Name"; Дети будут стрелять первыми, несмотря на то, что отправка сообщения nil ничего не даст, так что это нормально, что они играют со своими воображаемыми детьми. У них есть делегат, если им нужно отправить информацию или спросить что-то важное.

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

person Mike Sand    schedule 22.01.2015

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

Сказав это, я бы посоветовал против этого подхода, и я бы предложил:

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

    Если вы хотите, чтобы контроллер родительского представления имел доступ к данным, введенным в контроллеры дочернего представления, вы, очевидно, можете сделать так, чтобы контроллеры дочернего представления обновляли контроллер родительского представления (например, с использованием шаблона протокола делегирования). Но я бы лично не выставлял свойства UIKit дочернего элемента родительскому контроллеру представления (контроллеру представления не нужно обращаться к объектам UIKit представления другого контроллера), а скорее возвращал бы данные модели.

  • Если вам не нужны отдельные контроллеры, просто не помещайте эти дочерние представления в отдельные сцены. В любом случае ваш пример не кажется очень убедительным использованием сдерживания контроллера представления. На мой взгляд, эти дочерние сцены должны иметь разумную степень сложности, чтобы оправдать использование отдельной сцены (и, таким образом, оправдать отдельные контроллеры представления). Если они не так сложны, вы просто добавляете подпредставления в родительский контроллер представления, что намного проще, чем использовать представления-контейнеры и связанное с этим включение контроллера представления.

person Rob    schedule 22.01.2015