Как добавить подпредставление, которое имеет собственный UIViewController в Objective-C?
Я борюсь с подпредставлениями, которые имеют свои UIViewControllers
, у меня есть UIViewController
с видом (светло-розовый) и двумя кнопками на toolbar
, Я хочу, чтобы синий вид отображался при нажатии первой кнопки, а желтый вид, отображаемый при нажатии второй кнопки. Должно быть легко, если я просто хочу показать вид. Но синий вид будет содержать таблицу, поэтому ему нужен собственный контроллер. Это был мой первый урок. Я начал с этого ТАКОГО вопроса, где я узнал, что мне нужен контроллер для стола.
Итак, я собираюсь отступить и сделать несколько маленьких шагов здесь. Ниже приведено изображение простой отправной точки с моей утилитой ViewController
(контроллер основного вида) и два других контроллера (синий и желтый). Представьте, что когда утилита ViewController
(основной вид) в первую очередь отображается синий (по умолчанию) вид будет отображаться там, где расположен розовый вид. Пользователи смогут нажимать две кнопки для перехода вперед и назад, и розовый вид НИКОГДА не будет отображаться. Я просто хочу, чтобы синий вид шел туда, где розовый, а желтый вид там, где розовый. Я надеюсь это имеет смысл.
Я пытаюсь использовать addChildViewController
, Из того, что я видел, есть два способа сделать это: представление контейнера в storyboard
или же addChildViewController
программно. Я хочу сделать это программно. Я не хочу использовать NavigationController
или панель вкладок. Я просто хочу добавить контроллеры и засунуть правильный вид в розовый вид при нажатии соответствующей кнопки.
Ниже приведен код, который у меня есть. Все, что я хочу сделать, это показать синий вид, где розовый вид. Из того, что я видел, я должен быть в состоянии просто addChildViewController
и addSubView. Этот код не делает это для меня. Моя путаница становится лучше меня. Может кто-нибудь помочь мне показать синий вид там, где розовый вид?
Этот код не предназначен для выполнения чего-либо, кроме отображения синего представления в viewDidLoad.
IDUtilityViewController.h
#import <UIKit/UIKit.h>
@interface IDUtilityViewController : UIViewController
@property (strong, nonatomic) IBOutlet UIView *utilityView;
@end
IDUtilityViewController.m
#import "IDUtilityViewController.h"
#import "IDAboutViewController.h"
@interface IDUtilityViewController ()
@property (nonatomic, strong) IDAboutViewController *aboutVC;
@end
@implementation IDUtilityViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.aboutVC = [[IDAboutViewController alloc]initWithNibName:@"AboutVC" bundle:nil];
[self addChildViewController:self.aboutVC];
[self.aboutVC didMoveToParentViewController:self];
[self.utilityView addSubview:self.aboutVC.aboutView];
}
@end
--------------------------РЕДАКТИРОВАТЬ----------------------- -------
Self.aboutVC.aboutView равен нулю. Но я подключил его в storyboard
, Мне все еще нужно создать его экземпляр?
2 ответа
Этот пост, который относится к ранним временам современной iOS, обычно имеет самый последний синтаксис, такой как Swift 4 в настоящее время. Если вы начинаете с iOS, с автопрокладкой и т. Д., Это поможет вам.
В iOS сегодня "все является контейнером". Это основной способ создания приложений сегодня.
Приложение может быть настолько простым, что оно имеет только один вид. Но даже в этом случае каждая "вещь" на экране является контейнером.
Это так просто...
(A) Перетащите вид контейнера на свою сцену...
Перетащите вид контейнера в вид сцены. (Точно так же, как вы бы перетащили, скажем, UIButton.)
Контейнерный вид - коричневая вещь на этом изображении. Это на самом деле внутри вашей сцены.
Когда вы перетаскиваете вид контейнера в вид сцены, Xcode автоматически дает вам две вещи:
Вы получаете вид контейнера внутри вида сцены, и,
Вы получаете совершенно новый
UIViewController
который просто сидит где-то на вашей раскадровке.
Они связаны с "масонским символом" - объяснено ниже!
(B) Нажмите на этот новый контроллер вида (новая вещь, которую XCode сделал для вас где-то в белой области, а не на вашей сцене) ... и измените класс!
Это действительно так просто.
Вы сделали
Вот то же самое объясняется визуально.
Обратите внимание на вид контейнера в (A)
,
Обратите внимание на контроллер в (B)
,
Нажмите на B. (Это B - не A!)
Идите к инспектору в правом верхнем углу. Обратите внимание, что это говорит "UIViewController"
[ ] [3]
Измените его на свой собственный класс, который является UIViewController.
Итак, у меня есть класс Swift Snap
который является UIViewController
,
Поэтому там, где написано "UIViewController" в Инспекторе, я набрал "Snap".
(Как обычно, Xcode автоматически завершит "Snap", когда вы начнете набирать "Snap...".)
Это все, что нужно сделать - все готово.
Как изменить вид контейнера - скажем, на вид таблицы.
Поэтому, когда вы нажимаете, чтобы добавить представление контейнера, Apple автоматически предоставляет вам контроллер связанного представления, сидящий на раскадровке.
Как это происходит (2017): это делает UIViewController
по умолчанию.
Это глупо: нужно спросить, какой тип тебе нужен. Например, часто вам нужно табличное представление. Вот как это можно изменить на что-то другое:
На момент написания Xcode дает вам
UIViewController
по умолчанию. Допустим, вы хотитеUICollectionViewController
вместо:(i) Перетащите контейнерный вид на вашу сцену. Посмотрите на UIViewController на раскадровке, который XCode дает вам по умолчанию.
(ii) Перетащите новый
UICollectionViewController
в любом месте на главной белой области раскадровки.(iii) Щелкните по виду контейнера внутри вашей сцены. Нажмите инспектор соединений. Обратите внимание, что есть один "Triggered Segue". Наведите курсор мыши на "Triggered Segue" и обратите внимание, что Xcode выделяет все нежелательные UIViewController.
(iv) Нажмите "x", чтобы фактически удалить этот запущенный переход.
(v) Перетащите из этого запущенного Segue (viewDidLoad является единственным выбором) . Перетащите через раскадровку на свой новый UICollectionViewController. Отпустите, и появится всплывающее окно. Вы должны выбрать встроить.
(vi) Просто удалите все ненужные UIViewController. Вы сделали
Краткая версия: удалите ненужный UIViewController. Поставить новый UICollectionViewController
на раскадровке. Удерживая клавишу "Control", перетащите из: Connections представления контейнера - Segger Segue - viewDidLoad в ваш новый контроллер. Обязательно выберите "вставлять" во всплывающем окне.
Ввод текстового идентификатора...
У вас будет одна из этих масонских символик "квадрат в квадрате": она находится на "линии сгиба", соединяющей ваш контейнерный вид с контроллером вида.
"Масонский символ" - это тема.
Выберите переход, нажав на "масонский символ" вещь.
Посмотри направо.
Вы ДОЛЖНЫ ввести текстовый идентификатор для перехода.
Вы выбираете имя. Это может быть любая текстовая строка. Разумным выбором часто является "segueClassName".
Если вы будете следовать этому шаблону, все ваши сегменты будут называться segueClockView, seguePersonSelector, segueSnap, segueCards и так далее.
Далее, где вы используете этот текстовый идентификатор?
Как подключить "к" дочернему контроллеру...
Затем выполните следующее в коде в ViewController всей сцены.
Допустим, у вас есть три вида контейнера в сцене. Каждый контейнерный вид содержит свой контроллер, например "Snap", "Clock" и "Other".
Последний синтаксис Swift3 (2017)
var snap:Snap?
var clock:Clock?
var other:Other?
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "segueSnap")
{ snap = (segue.destination as! Snap) }
if (segue.identifier == "segueClock")
{ clock = (segue.destination as! Clock) }
if (segue.identifier == "segueOther")
{ other = (segue.destination as! Other) }
}
Это так просто. Вы подключаете переменную для обращения к контроллерам, используя prepareForSegue
вызов.
Как подключиться в "другом направлении", вплоть до родителя...
Скажем, вы "в" контроллере, который вы поместили в представление контейнера ("Snap" в примере) .
Попасть в контроллер представления "босс" над вами может быть непонятно (в данном примере "Dash") . К счастью, это так просто:
// Dash is the overall scene.
// Here we are in Snap. Snap is one of the container views inside Dash.
class Snap {
var myBoss:Dash?
override func viewDidAppear(_ animated: Bool) { // MUST be viewDidAppear
super.viewDidAppear(animated)
myBoss = self.parent as? Dash
}
Критическое: работает только от viewDidAppear
или позже. Не будет работать в viewDidLoad
,
Вы сделали
Важно: это работает только для контейнеров.
Важный продвинутый совет: не забывайте, что это работает только для контейнеров.
В наши дни с идентификаторами раскадровки, это обычное явление, когда просто выкладывают новые виды на экран (а не как в разработке для Android) . Итак, скажем, пользователь хочет что-то редактировать...
// let's just pop a view on the screen.
// this has nothing to do with container views
//
let e = ...instantiateViewController(withIdentifier: "Edit") as! Edit
e.modalPresentationStyle = .overCurrentContext
self.present(e, animated: false, completion: nil)
При использовании контейнерного представления ГАРАНТИРУЕТСЯ, что Dash будет родительским контроллером представления Snap.
Однако это НЕ ОБЯЗАТЕЛЬНО СЛУЧАЙ, когда вы используете instantiateViewController.
Весьма странно, что в iOS родительский контроллер представления не связан с классом, который его создал. (Это может быть то же самое, но обычно это не то же самое.) self.parent
шаблон только для контейнеров.
(Для аналогичного результата в шаблоне instantiateViewController вы должны использовать протокол и делегат, помня, что делегат будет слабым звеном.)
подготовить для плохо названного...
Стоит отметить, что "prepareForSegue" - это действительно плохое имя!
"prepareForSegue" используется для двух целей: загрузка представлений контейнера и переключение между сценами.
Но на практике вы очень редко переходите между сценами! Принимая во внимание, что почти каждое приложение имеет много-много контейнерных представлений как само собой разумеющееся.
Было бы гораздо больше смысла, если бы "prepareForSegue" называлось что-то вроде "loadingContainerView".
Больше одного...
Распространенная ситуация: у вас есть небольшая область на экране, где вы хотите показать один из нескольких различных контроллеров представления. Например, один из четырех виджетов.
Самый простой способ сделать это: просто иметь четыре разных контейнера, все они находятся в одной и той же области. В своем коде просто скройте все четыре и включите тот, который вы хотите видеть.
На раскадровке есть один пустой "держатель" UIView, который просто содержит четыре представления контейнера. Затем вы можете изменить размер или переместить все четыре сразу, определив размер или переместив "держатель". В вашем коде просто есть четыре UIView
розетки, по одному для каждого из представлений контейнера. Скопируйте и вставьте приведенный выше код "Как подключиться к дочернему контроллеру", чтобы подключить четыре контроллера представлений.
Примечание - ссылки на раскадровку приходят!
Как указывает SimplGy ниже: " Ссылки на раскадровку в iOS 9 делают представления контейнеров еще более привлекательными. Вы можете определять свое повторно используемое представление (контроллер), где хотите, и ссылаться на него из любого представления контейнера в нескольких модульных раскадровках".
Заметьте также, что - довольно запутанно - часто сегодня вы просто не беспокоитесь о просмотрах контейнеров!
Ты просто instantiateViewController#withIdentifier
во многих ситуациях.
Но обратите внимание на "готчя" о .parent
объяснил выше. Весь смысл представлений контейнера заключается в том, что вы мгновенно и просто уверены в родительской цепочке.
Если вы идете с instantiateViewController#withIdentifier
используя ссылку на раскадровку, вы должны возиться с протоколом и делегатом (помня, что делегат будет слабым звеном) . Но тогда вы можете использовать его "на лету" гибко в любом месте.
В отличие от использования "фиксированного", так сказать, контейнерного представления чрезвычайно просто, и вы мгновенно подключаетесь между родителем и потомком, как объяснено выше.
Я вижу две проблемы. Во-первых, поскольку вы создаете контроллеры в раскадровке, вы должны создавать их с помощью instantiateViewControllerWithIdentifier:
не initWithNibName:bundle:
, Во-вторых, когда вы добавляете представление как подпредставление, вы должны дать ему фрейм. Так,
- (void)viewDidLoad
{
[super viewDidLoad];
self.aboutVC = [self.storyboard instantiateViewControllerWithIdentifier:@"aboutVC"]; // make sure you give the controller this same identifier in the storyboard
[self addChildViewController:self.aboutVC];
[self.aboutVC didMoveToParentViewController:self];
self.aboutVC.view.frame = self.utilityView.bounds;
[self.utilityView addSubview:self.aboutVC.aboutView];
}