Ответ 1
1. Пусть докладчик запросит информацию из представления
Чтобы ответить на это, мы должны получить более подробную информацию о конкретном случае. Почему представление не может предоставлять больше контекстной информации непосредственно при обратном вызове?
Я предлагаю передать объект Presenter Command, чтобы Presenter не должен был знать, что делать в этом случае. Презентатор может выполнить объектный метод, передавая некоторую информацию самостоятельно, если это необходимо, не зная ничего о состоянии представления (и тем самым введя в него высокую связь).
- Вид находится в состоянии, которое вы вызываете x (против y и z). Он все равно знает о своем состоянии.
- Пользователь завершает действие. View сообщает своему делегату (Presenter) о завершении. Поскольку он задействован, он создает объект передачи данных, чтобы хранить всю обычную информацию. Одним из этих атрибутов DTO является
id<FollowUpCommand> followUpCommand
. View создаетXFollowUpCommand
(в отличие отYFollowUpCommand
иZFollowUpCommand
) и соответственно устанавливает его параметры, а затем помещает его в DTO. - Ведущий получает вызов метода. Он делает что-то с данными независимо от того, какой конкретный
FollowUpCommand
существует. Затем он выполняет только протокол,followUpCommand.followUp
. Конкретная реализация будет знать, что делать.
Если вам нужно выполнить оператор switch-case/if-else для некоторого свойства, большую часть времени он помог бы моделировать параметры как объекты, наследующие от общего протокола, и передать объекты вместо состояния.
2. Модальный модуль
Если модуль представления или представленный модуль решают, модально ли это? - Представленный модуль (второй) должен принять решение, если он предназначен только для использования в моделях. Поместите знания о вещи в самом деле. Если его режим представления зависит от контекста, ну, то сам модуль не может решить.
Второй каркас модуля получит следующее сообщение:
[secondWireframe presentYourStuffIn:self.viewController]
Параметр - это объект, для которого должна состояться презентация. Вы можете пройти вместе с параметром asModal
, если модуль предназначен для использования в обоих направлениях. Если есть только один способ сделать это, поместите эту информацию в затронутый модуль (тот, который представлен) сам.
Затем он сделает что-то вроде:
- (void)presentYourStuffIn:(UIViewController)viewController {
// set up module2ViewController
[self.presenter configureUserInterfaceForPresentation:module2ViewController];
// Assuming the modal transition is set up in your Storyboard
[viewController presentViewController:module2ViewController animated:YES completion:nil];
self.presentingViewController = viewController;
}
Если вы используете Storyboard Segues, вам придется делать что-то по-другому.
3. Иерархия навигации
Кроме того, скажем, что второй вид модуля вставляется в контроллер навигации, как следует обрабатывать действие "назад"?
Если вы идете "все VIPER", да, вам нужно перейти от представления к его каркасу и перейти к другому каркасу.
Чтобы передать данные из представленного модуля ( "Второе" ) в модуль представления ( "Первый" ), добавьте SecondDelegate
и реализуйте его в FirstPresenter
. Перед появлением представленного модуля он отправляет сообщение SecondDelegate
для уведомления об итогах.
"Не сражайтесь с каркасом". Возможно, вы можете использовать некоторые из тонкостей навигационного контроллера, пожертвовав чисткой VIPER. Segues уже шаг в направлении механизма маршрутизации. Посмотрите на VTDAddWireframe для методов UIViewControllerTransitioningDelegate
в каркасе, который вводит пользовательские анимации. Возможно, это поможет:
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
{
return [[VTDAddDismissalTransition alloc] init];
}
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented
presentingController:(UIViewController *)presenting
sourceController:(UIViewController *)source
{
return [[VTDAddPresentationTransition alloc] init];
}
Сначала я подумал, что вам нужно будет хранить стек каркасов, похожий на стек навигации, и что все "активные" каркасные модули связаны друг с другом. Но это не так. Каркасы управляют содержимым модуля, но стек навигации является единственным стеком на месте, представляющим, какой вид контроллера видимости видим.
4. Потоки сообщений
Если разные модули говорят только через каркас или через делегатов между ведущими?
Если вы прямо отправляете другому модулю B объект сообщение из Presenter A, что должно произойти тогда?
Так как представление получателя не отображается, анимация не может запускаться, например. Ведущему по-прежнему приходится ждать Wireframe/Router. Поэтому он должен вставить анимацию в очередь, пока она не станет активной снова. Это делает Presenter более состоятельным, что затрудняет работу с ним.
Архитектурно, подумайте о роли, которую играют модули. В архитектуре Ports/Adapters, из которой Clean Architecture норы некоторые концепции, проблема более очевидна. В качестве аналогии: компьютер имеет много портов. Порт USB не может связываться с портом LAN. Каждый поток информации должен быть проложен через ядро.
В чем заключается суть вашего приложения?
Есть ли у вас модель домена? У вас есть набор услуг, которые запрашиваются из разных модулей? Модули VIPER окружают вид. Компоненты модулей, как и механизмы доступа к данным, не принадлежат к определенному модулю. Это то, что вы можете назвать ядром. Там вы должны выполнить изменения данных. Если другой модуль становится видимым, он извлекает измененные данные.
Однако для простых целей анимации маршрутизатор знает, что делать и выдавать команду ведущему в зависимости от изменения модуля.
В коде VIPER Todo:
- "Список" - это корневой режим.
- В верхней части списка отображается представление "Добавить".
- ListPresenter реализует AddModuleDelegate. Если модуль "Добавить" закончен, ListPresenter будет знать, а не его каркас, потому что представление уже находится в стеке навигации.
5. Состояние хранения
Кто должен сохранять состояние текущего выбранного вывода, MapViewController, MapPresenter или MapWireframe, чтобы я мог знать, когда вернется, какой контакт должен изменить цвет?
Отсутствует. Избегайте состояния в ваших модулях просмотра, чтобы снизить затраты на поддержание вашего кода. Вместо этого попытайтесь выяснить, можете ли вы передать представление изменений контактов во время изменений.
Попытайтесь достичь сущностей для получения состояния (через Presenter и Interactor и еще много чего).
Это не означает, что вы создаете объект Pin
в своем уровне представления, передаете его из контроллера просмотра, чтобы просмотреть контроллер, изменить его свойства и затем отправить его обратно, чтобы отразить изменения. Будет ли NSDictionary
с сериализованными изменениями? Вы можете поместить новый цвет туда и отправить его с PinEditViewController
обратно в Presenter, который выдает изменение в MapViewController
.
Теперь я обманул: MapViewController
должно быть состояние. Он должен знать все контакты. Затем я предложил вам сменить словарь изменений, чтобы MapViewController
знал, что делать.
Но как вы идентифицируете затронутый контакт?
Каждый вывод может иметь свой собственный идентификатор. Возможно, этот идентификатор - это просто его местоположение на карте. Может быть, это его индекс в массиве контактов. В любом случае вам нужен какой-то идентификатор. Или вы создаете идентифицируемый объект-обертку, который держится на самом выводе в течение всего периода действия. (Это звучит слишком смешно с целью изменения цвета).
Отправка событий в состояние изменения
VIPER очень услужлив. Существует множество объектов, не имеющих гражданства, связанных друг с другом для передачи сообщений и преобразования данных. В сообщении Brigade Engineering также показан подход, ориентированный на данные.
Сущности находятся в довольно тонком слое. Напротив спектра, который я имею в виду, лежит Модель домена. Этот шаблон не нужен для каждого приложения. Моделирование ядра вашего приложения аналогичным образом может быть полезным для ответа на некоторые из ваших вопросов.
В отличие от Entities в качестве контейнеров данных, в которые каждый может связаться с помощью "менеджеров данных", домен защищает его объекты. Домен также будет информировать об изменениях. (Через NSNotificationCenter
, для стартеров. Меньше, так как прямые вызовы с помощью команды).
Теперь это может быть подходящим для вашего случая с PIN-кодом:
- PinEditViewController изменяет цвет булавки. Это изменение в компоненте пользовательского интерфейса.
- Изменение компонента пользовательского интерфейса соответствует изменению вашей базовой модели. Вы выполняете изменения через стек модуля VIPER. (Сохраняете ли вы цвета? Если нет, объект
Pin
всегда недолговечен, но он все еще является сущностью, потому что имеет значение его идентификация, а не только его значения.) - Соответствующий
Pin
изменил цвет и опубликовал уведомление черезNSNotificationCenter
. - Случайностью (то есть
Pin
не знает), некоторые Interactor подписываются на эти уведомления и меняют внешний вид своего вида.
Хотя это может сработать и для вашего дела, я думаю, что привязка редактирования