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

Связывание контроллеров дочерних представлений с контроллером родительского представления в раскадровке

Можно ли связать контроллеры дочерних представлений с настраиваемым контроллером контейнера в Storyboard?

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

Что мне делать с контейнером VC для принятия дочерних VC?

4b9b3361

Ответ 1

Как что-то вроде комбо из ответов Калеба и Мэтта, я сделал:

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    if ([segue.identifier isEqualToString:@"cpdc_check_embed"]) {
        self.checkVC = segue.destinationViewController;
    }
}

... где checkVC - свойство на контроллере контейнера:

@property (weak,nonatomic) PXPCheckViewController * checkVC;

Вам просто нужно установить embed segue Storyboard ID на все, что вам нужно (в этом случае cpdc_check_embed):

check embed screen in Xcode

... и затем проверьте идентификатор в -prepareForSegue:sender:.

Все еще не выход, но более чистый, чем Мэтт (IMHO) и более конкретный, чем Caleb's, и вы все равно получаете красивую раскадровку:

enter image description here

Ответ 2

Раскадровка имеет дело со встроенными контроллерами представления контейнеров очень хорошо, отображая segues для контроллеров представления child/root, так что отношения четко отображаются. Также приятно, что дочерние и родительские контроллеры представлений разделены на разные сцены.

Если вы хотите достичь этого эффекта в своем собственном проекте, тогда есть трюк, который не идеален, но очень прост. В моем примере предположим, что у меня есть контроллер представления контейнера, который действует как контроллер панели вкладок с двумя вкладками "слева" и "справа". Я хочу, чтобы сцена представляла родительский контроллер представления, а две отдельные сцены представляют собой как "левый" контроллер детского представления, так и "правый" контроллер детского представления.

Несмотря на то, что это невозможно, было бы неплохо, если бы я мог создать IBOutlet из контроллера контейнера для своих детей в разных сценах, а затем, когда отобразится мой диспетчер просмотра контейнера, установите родительские/дочерние отношения в соответствии с правила, описанные в документации UIViewController. Если бы у нас были ссылки на наши "левые" и "правильные" дочерние контроллеры, мы могли бы установить отношения без проблем.

Стандартным решением этой проблемы ссылок является создание ссылок на контроллеры дочерних представлений путем перетаскивания в выходы Object в сцену контроллера контейнера, а затем определение их типа класса как экземпляров классов контроллера детского представления.

Однако для того, чтобы дети были отделены друг от друга в разных сценах, таких как встроенные контейнеры Apple, мы будем использовать другой трюк. Во-первых, предположим, что у нас есть следующие свойства, объявленные в нашем классе контейнеров, ContainerViewController:

@property (nonatomic, strong, readwrite) UIViewController *leftViewController;
@property (nonatomic, strong, readwrite) UIViewController *rightViewController;

В нашей раскадровке выберите сцену, представляющую "левый" контроллер вида. В инспекторе атрибутов установите для свойства контроллера identifier значение "cvc_leftViewController" ( "cvc_" относится к ContainerViewController, но на самом деле идентификатор может быть любым, что вы хотите). Сделайте то же самое для правой сцены контроллера, установив его идентификатор на "cvc_rightViewController".

Теперь вставьте следующий код в метод ContainerViewController viewDidLoad:

if (self.storyboard) {
    _leftViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"cvc_leftViewController"];
    _rightViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"cvc_rightViewController"];
}

Когда ContainerViewController загружается из раскадровки, он будет захватывать "левые" и "правильные" контроллеры представлений из своих соответствующих сцен и устанавливать ссылки на них через свои свойства. Теперь, когда вы контролируете экземпляры контроллера дочернего представления, вы можете настроить родительские/дочерние отношения, как вам нравится. Чтобы узнать, как это сделать, обратитесь к документации UIViewController.

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

Изменить: Хотя это совершенно не нужно и ничего не значит, если вы действительно хотите, чтобы раскадровка отображала соединения с вашим контейнером на контроллеры вашего дочернего представления, как и в Apple, встроенные контейнеры, просто используйте мой метод выше и затем настройте segues непосредственно между сценой контейнера на детские сцены и просто никогда не выполняйте эти сеансы. Теперь все будет работать правильно и выглядеть довольно.

Ответ 3

Можно ли связать контроллеры дочерних представлений с пользовательским представлением контейнера контроллер в раскадровке?

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

Вероятно, вы спрашиваете об этом, потому что хотите передать некоторую информацию с одного контроллера представления на другой, когда вы переходите с одной сцены на другую. Способ сделать это, когда вы работаете с раскадными версиями, состоит в том, чтобы переопределить -prepareForSegue:sender: в одном или обоих контроллерах представления, затронутых segue. Объект UIStoryboardSegue, указанный в параметре segue, имеет свойства sourceViewController и destinationViewController, а также свойство identifier. Вы можете использовать эти свойства, чтобы идентифицировать сегмент, который собирается передавать данные между контроллерами вида.

В блоге Ray Wenderlich есть хороший двухуровневый учебник по использованию раскадровки, который может вам помочь:

  • Часть 1 охватывает настройку проекта раскадровки, добавление сцен и создание сегментов.

  • Часть 2 посвящена использованию segues для перехода между сценами, включая метод prepareForSeque, упомянутый выше.

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

Ответ 4

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

Этот ответ Как создать пользовательский контейнер контроллера просмотра с помощью раскадровки в iOS 5, я должен надеяться. Ответ также дает пример рабочего приложения, который очень полезен.

Ответ 5

Проблема с ответом @Ben (в противном случае разумным) заключается в том, что он работает только на одном уровне вложенности. Помимо этого потребовалось бы, чтобы каждый последующий VC настраивался для сохранения контроллера представления вложенности в prepareForSegue.

Чтобы решить эту проблему, я потратил слишком много времени на изучение индекса на основе NSObject, который вы могли бы добавить в Storyboard, привязать к сцене и затем зарегистрировать его родительский VC в глобальном индексе на основе типа и restoreId. Это работает/может работать, но в конечном итоге это слишком много усилий и по-прежнему требует двухэтапного процесса визуальной привязки и программного поиска.

Для меня самым простым и наиболее общим решением является ленивое схождение иерархии контроллера представления

В моем простом тестовом проекте я добавил следующие строки для просмотраDidLoad:

    self.left.data = [
        "Zombie ipsum reversus ab viral inferno, nam rick grimes malum cerebro.",
        "De carne lumbering animata corpora quaeritis." ]

где left определяется как:

lazy var left:CollectionViewController = { [unowned self] in
    return self.childViewControllerWithId("Left") as! CollectionViewController }()

и childViewControllerWithId определяется как:

extension UIViewController {
    func childViewControllerWithId(rid:String) -> UIViewController? {

        // check immediate child controllers
        for vc in self.childViewControllers as! [UIViewController] {
            if vc.restorationIdentifier == rid { return vc }
        }

        // check nested controllers
        for vc in self.childViewControllers as! [UIViewController] {
            if let vc = vc.childViewControllerWithId(rid) {
                return vc
            }
        }

        assert(false, "check your assumptions")
        return nil
    }
}

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

И чтобы указать, что, как мы надеемся, очевидно, вам не нужно реализовывать prepareForSegue, и вам не нужно использовать ленивую загрузку, вам просто нужно позвонить find(...).