Координирование усилий между контроллерами представления
Немного приложений для iOS показывают только сингл, экранный из содержания. Вместо этого они показывают некоторое содержание когда сначала запущенный и затем показывают и скрывают другое содержание в ответ на пользовательские действия. Эти переходы обеспечивают единственный унифицированный пользовательский интерфейс, которые выводят на экран много содержания, просто не одновременно.
Условно, мелкими кусочками содержания управляют классы контроллера другого представления. Это соглашение кодирования позволяет Вам создавать меньшие и более простые классы контроллера, которые просто реализовать. Однако деление работы между многократными классами налагает дополнительные требования на Ваши проекты класса. Для поддержания иллюзии единственного интерфейса контроллеры представления должны обмениваться сообщениями и данными для координирования переходов от контроллера до другого. Таким образом, как раз когда Ваши классы контроллера представления смотрят внутрь, чтобы управлять представлениями и выполнить задачи, присвоенные им, они также смотрят за пределы для передачи с другими сотрудничающими контроллерами представления.
Когда происходит координация между контроллерами представления
Коммуникация между контроллерами представления связывается к роли те контроллеры представления игра в Вашем приложении. Было бы невозможно описать все возможные взаимодействия между контроллерами представления, потому что число и природа этих отношений зависят от проекта Вашего приложения. Однако возможно описать, когда эти взаимодействия происходят и дать некоторые примеры видов координации, которая могла бы иметь место в Вашем приложении.
Время жизни контроллера представления имеет три этапа, во время которых оно могло бы скоординировать с другими объектами:
Инстанцирование контроллера представления. На этом этапе, когда контроллер представления создается, существующий контроллер представления или другой объект были ответственны за его создание. Обычно, этот объект знает, почему контроллер представления создавался и какую задачу это должно выполнить. Таким образом, после того, как контроллер представления инстанцируют, это намерение должно быть передано к нему.
Точные подробные данные этой начальной конфигурации варьируются. Иногда, существующий контроллер представления передает объекты данных новому контроллеру. В других случаях это может сконфигурировать стиль презентации для этого или установить прочные связи между двумя контроллерами представления. Эти ссылки позволяют дальнейшую коммуникацию позже во времени жизни контроллера представления.
Во время времени жизни контроллера представления. На этом этапе некоторые контроллеры представления связываются с другими контроллерами представления во время их времени жизни. Получатель этих сообщений мог быть контроллером представления, создавшим его, коллеги с подобными временами жизни, или даже новый контроллер представления, который это само создало. Вот несколько общих проектов:
Контроллер представления отправляет уведомления, что пользователь выполнил определенное действие. Поскольку это - уведомление, объект, получающий это сообщение, просто уведомляется, что что-то произошло.
Контроллер представления отправляет данные в другой контроллер представления. Например, контроллер панели вкладок не устанавливает встроенные отношения между своими дочерними элементами, но Ваше приложение могло бы установить такие отношения, когда вкладки выводят на экран тот же объект данных, просто по-разному. Когда пользователь оставляет одну вкладку, контроллер представления, связанный с той вкладкой, отправляет информацию о выборе в контроллер представления, собирающийся быть выведенным на экран. В свою очередь, новый контроллер представления использует эти данные для конфигурирования его представлений так, чтобы переход казался бесшовным. В данном случае никакой новый контроллер представления не инстанцирует действие. Вместо этого два контроллера представления являются коллегами с тем же временем жизни и могут продолжать координировать, как пользователь переключается между ними.
Контроллер представления отправляет сообщения для предоставления других полномочий контроллера представления над его действиями. Например, если контроллер представления позволяет пользователям вводить данные, он мог бы отправить сообщения, чтобы позволить другому контроллеру решать, допустимы ли данные вводимый пользователь. Если данные недопустимы, контроллер представления может запретить пользователю от принятия недопустимых данных или скорректировать свой интерфейс для отображения ошибки.
Уничтожение контроллера представления. Когда их задача завершается, на этом этапе много контроллеров представления отправляют сообщения. Эти сообщения распространены, потому что соглашение для контроллера, создавшего контроллер представления, чтобы также выпустить его. Иногда, эти сообщения просто передают это, пользователь закончил задачу. В других случаях, такой как тогда, когда выполняемая задача генерировала новые объекты данных, сообщение передает новые данные назад к другому контроллеру.
Во время времени жизни контроллера представления ему свойственно обменяться информацией с другими контроллерами представления. Эти сообщения используются для уведомления других контроллеров, когда вещи происходят, отправьте им данные, или даже попросите, чтобы они осуществили контроль над действиями контроллера.
С Раскадровками Сконфигурирован Контроллер Представления, Когда Он Инстанцируют
Раскадровки предоставляют прямую поддержку для конфигурирования недавно инстанцированных контроллеров, прежде чем они будут выведены на экран. Когда раскадровка инстанцирует новых контроллеров представления автоматически, она вызывает объект в Вашем приложении, чтобы позволить ему конфигурировать новый контроллер или создавать ссылки к или от нового контроллера. Когда Ваше приложение сначала запускается, делегат приложения конфигурирует начальный контроллер представления. Когда переход инициирован, исходный контроллер представления конфигурирует целевой контроллер представления.
Существует несколько соглашений, используемых для реализации целевых контроллеров представления:
Целевой контроллер представления представляет свойства, и методы раньше конфигурировали его.
Целевой контроллер представления связывается как можно меньше с контроллерами представления, которые он не создавал лично. Когда это делает так, эти каналы связи должны использовать делегацию. Целевой делегат контроллера представления сконфигурирован как одно из его свойств.
Тщательно после этих соглашений помогает организовать Ваш код конфигурации и тщательно ограничивает направление зависимостей между классами контроллера представления в Вашем приложении. Путем изоляции зависимостей в приложении Вы увеличиваете возможность для повторного использования кода. Вы также контроллеры режима конструктора, которые проще протестировать в изоляции от остальной части Вашего приложения.
Конфигурирование начального контроллера представления в запуске
При определении основной раскадровки в проекте iOS автоматически выполняет большую работу для Вас для установки приложения. Когда Ваше приложение вызывает UIApplicationMain
функция, iOS выполняет следующие действия:
Это инстанцирует делегата приложения на основе имени класса, которое Вы передали в
UIApplicationMain
функция.Это создает новое окно, присоединенное к основному экрану.
Если Ваш делегат приложения реализует a
window
свойство, iOS устанавливает это свойство в новое окно.Это загружает основную раскадровку, на которую ссылаются в информационном файле списка свойств приложения.
Это инстанцирует начального контроллера представления основной раскадровки.
Это устанавливает окно
rootViewController
свойство к новому контроллеру представления.Это вызывает делегата приложения
application:didFinishLaunchingWithOptions:
метод. Ваш делегат приложения, как ожидают, сконфигурирует начальный контроллер представления (и его дочерние элементы, если это будет контейнерный контроллер представления).Это вызывает окно
makeKeyAndVisible
метод для отображения окна.
Перечисление 11-1 показывает реализацию application:didFinishLaunchingWithOptions:
метод из Вашего Второго приложения для iOS: учебное руководство по Раскадровкам. В этом примере начальный контроллер представления раскадровки является контроллером навигации с пользовательским контроллером содержания, выводящим на экран основное представление. Код сначала получает ссылки на контроллер представления, которым он интересуется. Затем это выполняет любую конфигурацию, которая не могла быть выполнена в Интерфейсном Разработчике. В этом примере пользовательский объект контроллера данных предоставлен для основного контроллера представления пользовательским объектом контроллера данных.
Перечисление 11-1 делегат приложения конфигурирует контроллер
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions |
{ |
UINavigationController *navigationController = (UINavigationController*) self.window.rootViewController; |
BirdsMasterViewController * firstViewController = [[navigationController viewControllers] objectAtIndex:0]; |
BirdSightingDataController *dataController = [[BirdSightingDataController alloc] init]; |
firstViewController.dataController = dataController; |
return YES; |
} |
Если Ваш проект не идентифицирует основную раскадровку, UIApplicationMain
функция создает делегата приложения и вызывает его, но не выполняет ни одного из других шагов, описанных ранее. Необходимо было бы записать код для выполнения тех шагов сами. Перечисление 11-2 показывает код, который Вы могли бы реализовать, если необходимо было выполнить эти шаги программно.
Перечисление 11-2 , Создающее окно, когда не используется основная раскадровка
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions |
{ |
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; |
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MyStoryboard" bundle:nil]; |
MainViewController *mainViewController = [storyboard instantiateInitialViewController]; |
self.window.rootViewController = mainViewController; |
// Code to configure the view controller goes here. |
[self.window makeKeyAndVisible]; |
return YES; |
} |
Конфигурирование Контроллера назначения, Когда Инициирован Переход
когда переход инициирован, iOS выполняет следующие задачи:
Это инстанцирует целевого контроллера представления.
Это инстанцирует нового объекта перехода, содержащего всю информацию для инициированного перехода.
Это вызывает исходный контроллер представления
prepareForSegue:sender:
метод, передающий в новом переходе, возражает и объект, инициировавший переход.Это вызывает переход
perform
метод для обеспечения контроллера назначения на экран. Фактическое поведение зависит от вида выполняемого перехода. Например, модальный переход говорит исходному контроллеру представления представлять целевой контроллер представления.Это выпускает объект перехода, и переход завершен.
Исходный контроллер представления prepareForSegue:sender:
если целевой контроллер представления реализует тот, метод выполняет любую необходимую конфигурацию целевых свойств контроллера представления, включая делегата.
Перечисление 11-3 показывает реализацию prepareForSegue:sender:
метод из Вашего Второго приложения для iOS: учебное руководство по Раскадровкам.
Перечисление 11-3 , Конфигурирующее контроллер назначения в переходе
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender |
{ |
if ([[segue identifier] isEqualToString:@"ShowSightingsDetails"]) |
{ |
DetailViewController *detailViewController = [segue destinationViewController]; |
detailViewController.sighting = [self.dataController objectInListAtIndex:[self.tableView indexPathForSelectedRow].row]; |
} |
if ([[segue identifier] isEqualToString:@"ShowAddSightingView"]) |
{ |
AddSightingViewController *addSightingViewController = [[[segue destinationViewController] viewControllers] objectAtIndex:0]; |
addSightingViewController.delegate = self; |
} |
} |
Эта реализация, от основного контроллера представления для приложения, фактически обрабатывает два различных перехода, сконфигурированные в раскадровке. Это различает два перехода с помощью перехода identifier
свойство. В обоих случаях это следует соглашению кодирования, установленному ранее первым получением контроллера представления и затем конфигурированием его.
Когда переход к подробному контроллеру представления, переход произошел, потому что пользователь выбрал строку в табличном представлении. В этом случае код передает достаточно данных целевому контроллеру представления так, чтобы целевой контроллер представления мог вывести на экран наблюдение. Код использует выбор пользователя для получения прицеливающегося объекта от основного контроллера данных контроллера представления. Это тогда присваивает это наблюдение контроллеру назначения.
В другом случае новый контроллер представления позволяет пользователю добавлять новое наблюдение за птицей. Никакие данные не должны быть отправлены в этот контроллер представления. Когда пользователь заканчивает вводить данные, Однако основной контроллер представления должен получить данные. Для получения той информации исходный контроллер представления реализует протокол делегата, определенный контроллером представления Add (не показанный здесь), и делает себя целевым делегатом контроллера представления.
Используя делегацию для передачи с другими контроллерами
В основанной на делегате модели контроллер представления определяет протокол для своего делегата в реализации. Протокол определяет методы, которые вызывает контроллер представления в ответ на определенные действия, такие как касания в кнопке Done. Делегат тогда ответственен за реализацию этих методов. Например, когда представленный контроллер представления заканчивает свою задачу, он отправляет сообщение в контроллер представления представления, и тот контроллер отклоняет его.
Используя делегацию для управления взаимодействиями с другими объектами приложения имеет главные преимущества по другим методам:
Объект делегата имеет возможность проверить или включить изменения от контроллера представления.
Использование делегата способствует лучшей инкапсуляции, потому что контроллер представления ничего не должен знать о классе делегата. Это позволяет Вам снова использовать тот контроллер представления в других частях Вашего приложения.
Для иллюстрирования реализации протокола делегата рассмотрите пример контроллера представления рецепта, использовавшийся в Представлении Контроллера Представления и Выборе Transition Style. В том примере приложение рецептов представило контроллер представления в ответ на пользователя, желающего добавить новый рецепт. До представления контроллера представления контроллер текущего представления сделал себя делегатом RecipeAddViewController
объект. Перечисление 11-4 показывает определение протокола делегата для RecipeAddViewController
объекты.
Делегат перечисления 11-4 протокол для отклонения представленного контроллера представления
@protocol RecipeAddDelegate <NSObject> |
// recipe == nil on cancel |
- (void)recipeAddViewController:(RecipeAddViewController *)recipeAddViewController |
didAddRecipe:(MyRecipe *)recipe; |
@end |
Когда пользователь касается кнопки Cancel или Done в новом интерфейсе рецепта, RecipeAddViewController
вызовы объектов предыдущий метод на его объекте делегата. Делегат тогда ответственен за решение что план действий взять.
Перечисление 11-5 показывает реализацию метода делегата, обрабатывающего добавление новых рецептов. Этот метод реализован контроллером представления, представившим RecipeAddViewController
объект. Если пользователь принял новый рецепт — т.е. объект рецепта не nil
— этот метод добавляет рецепт к своим внутренним структурам данных и говорит его табличному представлению обновлять себя. (Табличное представление впоследствии перезагружает данные рецепта от того же recipesController
объект, показанный здесь.) Тогда метод делегата отклоняет представленный контроллер представления.
Перечисление 11-5 Отклоняя представленный контроллер представления с помощью делегата
- (void)recipeAddViewController:(RecipeAddViewController *)recipeAddViewController |
didAddRecipe:(Recipe *)recipe { |
if (recipe) { |
// Add the recipe to the recipes controller. |
int recipeCount = [recipesController countOfRecipes]; |
UITableView *tableView = [self tableView]; |
[recipesController insertObject:recipe inRecipesAtIndex:recipeCount]; |
[tableView reloadData]; |
} |
[self dismissViewControllerAnimated:YES completion: nil]; |
} |
Инструкции для управления данными контроллера представления
Тщательно управляя, как потоки данных и потоки управления между Вашими контроллерами представления критически важны по отношению к пониманию, как Ваше приложение работает и предотвращение тонких ошибок. Рассмотрите следующие инструкции при разработке контроллеров представления:
Целевое представление ссылки контроллера на данные приложения должно прибыть из исходного контроллера представления, если целевой контроллер представления не представляет автономное (и поэтому самоконфигурирующий) контроллер представления.
Выполните как можно больше конфигурации, использующей Интерфейсного Разработчика, вместо того, чтобы конфигурировать Ваш контроллер программно в Вашем коде.
Всегда используйте делегата для передачи информации назад к другим контроллерам. Ваш довольный контроллер представления никогда не должен должен быть знать класс исходного контроллера представления или любых контроллеров, которые это не создает.
Избегите ненужных соединений с объектами, внешними к Вашему контроллеру представления. Каждое соединение представляет зависимость, делающую его тяжелее для изменения проекта приложения.
Например, дочерние элементы контроллера навигации должны знать о родительском контроллере навигации и одноуровневых элементов сразу выше и ниже их на штабеле. Они редко должны связываться с другими одноуровневыми элементами.