Обнаруживать, когда представленный диспетчер просмотра отклонен
Скажем, у меня есть экземпляр класса контроллера вида VC2. В VC2 есть кнопка "Отмена", которая будет отклонена. Но я не могу обнаружить или получить обратный вызов, когда кнопка "отменить" получила триггер. VC2 - черный ящик.
Контроллер вида (называемый VC1) будет представлять VC2 с использованием метода presentViewController:animated:completion:
.
Какие параметры VC1 должны обнаружить, когда VC2 был уволен?
Изменить: из комментария @rory mckinnel и ответа @NicolasMiari я попробовал следующее:
В VC2:
-(void)cancelButton:(id)sender
{
[self dismissViewControllerAnimated:YES completion:^{
}];
// [super dismissViewControllerAnimated:YES completion:^{
//
// }];
}
В VC1:
//-(void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion
- (void)dismissViewControllerAnimated:(BOOL)flag
completion:(void (^ _Nullable)(void))completion
{
NSLog(@"%s ", __PRETTY_FUNCTION__);
[super dismissViewControllerAnimated:flag completion:completion];
// [self dismissViewControllerAnimated:YES completion:^{
//
// }];
}
Но dismissViewControllerAnimated
в VC1 не вызывался.
Ответы
Ответ 1
Согласно документам, диспетчер представления несет ответственность за фактическое увольнение. Когда представленный диспетчер уволит себя, он попросит ведущего сделать это за него. Поэтому, если вы переопределите функцию rejectViewControllerAnimated в контроллере VC1, я верю, что он будет вызван, когда вы нажмете cancel на VC2. Обнаружите увольнение, а затем вызовите версию суперклассов, которая будет действительно увольнять.
Как видно из обсуждения, это не работает. Вместо того, чтобы полагаться на основной механизм, вместо вызова dismissViewControllerAnimated:completion
на самом VC2, вызовите dismissViewControllerAnimated:completion
on self.presentingViewController
в VC2. Затем это вызовет ваше переопределение напрямую.
Лучшим подходом в целом было бы обеспечить, чтобы VC2 обеспечивал блок, который вызывается, когда модальный контроллер завершен.
Итак, в VC2 укажите свойство блока с именем onDoneBlock
.
В VC1 вы представляете следующее:
-
В VC1 создайте VC2
-
Установите обработчик для VC2 как: VC2.onDoneBlock={[VC2 dismissViewControllerAnimated:YES completion:nil]};
-
Представьте контроллер VC2 как обычно, используя [self presentViewController: VC2 анимированный: ДА завершение: ноль];
-
В VC2 в вызове отмены целевого действия self.onDoneBlock();
В результате VC2 сообщает, кто бы это ни делал, что он сделан. Вы можете расширить onDoneBlock
, чтобы иметь аргументы, указывающие, удалось ли модальное комлетирование, аннулирование и т.д....
Ответ 2
Использовать свойство блокировки
Объявить в VC2
var onDoneBlock : ((Bool) -> Void)?
Настройка в VC1
VC2.onDoneBlock = { result in
// Do something
}
Вызов в VC2, когда вы собираетесь отклонить
onDoneBlock!(true)
Ответ 3
Контроллер представляемого представления и могут представлять dismissViewController:animated:
, чтобы отклонить представленный контроллер представления.
Первый вариант (возможно) является "правильным" в плане дизайна: один и тот же "родительский" контроллер представления отвечает как за представление, так и за отклонение модального ("дочернего") контроллера представления.
Однако последний вариант более удобен: обычно кнопка "отклонить" прикрепляется к представленному представлению контроллера представления, и в качестве цели его действия указан контроллер представления.
Если вы применяете прежний подход, вы уже знаете строку кода в своем представительном контроллере представления, где происходит увольнение: либо запустите ваш код сразу после dismissViewControllerAnimated:completion:
, либо в пределах блока завершения.
Если вы принимаете последний подход (представленный контроллер представления отклоняет себя), имейте в виду, что вызов dismissViewControllerAnimated:completion:
из представленного контроллера представления приводит к тому, что UIKit, в свою очередь, вызывает этот метод на текущем контроллере представления:
Discussion
Представляющий контроллер представления отвечает за отклоняя представленный контроллер. Если вы вызываете этот метод на представленном контроллере вида UIKit просит представить просмотр контроллера для обработки увольнения.
(источник: описание класса UIViewController)
Итак, чтобы перехватить такое событие, вы можете переопределить этот метод в представляемом контроллере представления:
override func dismiss(animated flag: Bool,
completion: (() -> Void)?) {
super.dismiss(animated: flag, completion: completion)
// Your custom code here...
}
Ответ 4
Существует специальное Логическое свойство внутри UIViewController
называется isBeingDismissed
, которые вы можете использовать для этой цели:
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if isBeingDismissed {
// TODO: Do your stuff here.
}
}
Ответ 5
Вы можете использовать unind segue для выполнения этой задачи, не нужно использовать функцию rejectModalViewController. Определите метод размотки segue в вашем VC1.
См. эту ссылку о том, как создать раскручивание segue, fooobar.com/questions/14221/....
Предполагая, что ваш режим размотки настроен, в методе действий, определенном для вашей кнопки "Отмена", вы можете выполнить сеанс как -
[self performSegueWithIdentifier:@"YourUnwindSegueName" sender:nil];
Теперь, всякий раз, когда вы нажимаете кнопку "Отмена" в VC2, он будет отклонен и появится VC1. Он также будет вызывать метод разматывания, определенный в VC1. Теперь вы знаете, когда представленный контроллер просмотра отклонен.
Ответ 6
Я использую следующее, чтобы сообщить координатору, что контроллер просмотра "сделан". Это используется в подклассе AVPlayerViewController
в приложении tvOS и вызывается после завершения перехода на увольнение игрока:
class PlayerViewController: AVPlayerViewController {
var onDismissal: (() -> Void)?
override func beginAppearanceTransition(_ isAppearing: Bool, animated: Bool) {
super.beginAppearanceTransition(isAppearing, animated: animated)
transitionCoordinator?.animate(alongsideTransition: nil,
completion: { [weak self] _ in
if !isAppearing {
self?.onDismissal?()
}
})
}
}
Ответ 7
@user523234 - "Но отклонениеViewControllerAnimated в VC1 не вызывалось."
Вы не можете предположить, что VC1 на самом деле делает презентацию - это может быть контроллер корневого представления, VC0. В нем задействовано 3 контроллера:
- sourceViewController
- presentingViewController
- presentedViewController
В вашем примере VC1 = sourceViewController
, VC2 = presentedViewController
, ?? = presentingViewController
- возможно, VC1, возможно, нет.
Однако вы всегда можете полагаться на вызываемый VC1.animationControllerForDismissedController(если вы реализовали методы делегата) при отклонении VC2, и в этом методе вы можете делать то, что хотите, с помощью VC1
Ответ 8
Использование willMove(toParent: UIViewController?)
Следующим образом показалось мне willMove(toParent: UIViewController?)
. (Проверено на iOS12).
override func willMove(toParent parent: UIViewController?) {
super.willMove(toParent: parent);
if parent == nil
{
// View controller is being removed.
// Perform onDismiss action
}
}
Ответ 9
- Создайте один файл класса (.h/.m) и назовите его: DismissSegue
-
Выберите подкласс: UIStoryboardSegue
-
Перейдите в файл DismissSegue.m и запишите следующий код:
- (void)perform {
UIViewController *sourceViewController = self.sourceViewController;
[sourceViewController.presentingViewController dismissViewControllerAnimated:YES completion:nil];
}
-
Откройте раскадровку, а затем Ctrl + перетащите ее с кнопки отмены на VC1 и выберите "Действие Segue as Dismiss" и вы закончите.
Ответ 10
Вы можете обращаться с закрытием uiviewcontroller с помощью Unwind Segues.
https://developer.apple.com/library/content/technotes/tn2298/_index.html
https://spin.atomicobject.com/2014/12/01/program-ios-unwind-segue/
Ответ 11
override
viewDidAppear
сделал viewDidAppear
дело для меня. Я использовал синглтон в моём модале и теперь могу устанавливать и получать его из вызывающего VC, модального и везде.
Ответ 12
Переопределить функцию viewWillDisappear
в представленном контроллере представления.
override func viewWillDisappear(_ animated: Bool) {
//Your code here
}
Ответ 13
Как уже упоминалось, решение состоит в том, чтобы использовать override func dismiss(animated flag: Bool, completion: (() → Void)? = nil)
.
Для тех, кто интересуется, почему override func dismiss(animated flag: Bool, completion: (() → Void)? = nil)
не всегда работает, вы можете обнаружить, что вызов перехватывается UINavigationController
если им управляют. Я написал подкласс, который должен помочь:
class DismissingNavigationController: UINavigationController { override func dismiss(animated flag: Bool, completion: (() → Void)? = nil) { super.dismiss(animated: flag, completion: completion) topViewController?.dismiss(animated: flag, completion: completion) } }
Ответ 14
Я видел этот пост очень много раз, когда имел дело с этой проблемой, и я подумал, что смогу наконец пролить свет на возможный ответ.
Если вам нужно знать, вызывало ли пользовательские действия (например, жесты на экране) увольнение для UIActionController, и не хотите тратить время на создание подклассов или расширений или чего-либо еще в вашем коде, есть альтернатива.
Как выясняется, popoverPresentationController свойство UIActionController (или, скорее, любой UIViewController к тому эффекту), имеет делегат можно установить в любое время в вашем коде, который имеет тип UIPopoverPresentationControllerDelegate, и имеет следующие методы:
Назначьте делегата из вашего контроллера действий, внедрите выбранные вами методы в класс делегата (view, view controller или любой другой) и вуаля!
Надеюсь это поможет.
Ответ 15
Если вы хотите обработать отклонение контроллера представления, вы должны использовать код ниже.
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
if (self.isBeingDismissed && self.completion != NULL) {
self.completion();
}
}
К сожалению, мы не можем вызвать завершение в переопределенном методе - (void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^ _Nullable)(void))completion;
потому что этот метод вызывается, только если вы вызываете метод dismiss этого контроллера представления.
Ответ 16
Если вы переопределили на управляющем контроллере вида:
override func removeFromParentViewController() {
super.removeFromParentViewController()
// your code here
}
По крайней мере, это сработало для меня.