检测呈现的视图控制器何时被解除

假设我有一个名为VC2的视图控制器类的实例。 在VC2中,有一个“取消”按钮,它会自动解除。 但是当“取消”按钮被触发时,我无法检测或接收任何回调。 VC2是一个黑盒子。

视图控制器(称为VC1)将使用presentViewController:animated:completion:方法呈现VC2。

当VC2被解雇时,VC1必须检测哪些选项?

编辑:从@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:^{ // // }]; } 

但VC1中的dismissViewControllerAnimated没有被调用。

根据文档,呈现控制器负责实际解雇。 当呈现的控制器自我解散时,它将要求演示者为此进行操作。 因此,如果你在VC1控制器中覆盖dismissViewControllerAnimated,我相信当你在VC2上点击取消时它会被调用。 检测解雇,然后调用将执行实际解雇的超类版本。

从讨论中发现,这似乎不起作用。 而不是依赖于底层机制,而不是在VC2本身上调用dismissViewControllerAnimated:completion ,在VC2中的dismissViewControllerAnimated:completion上调用dismissViewControllerAnimated:completion 。 然后,这将直接调用您的覆盖。

一个更好的方法是让VC2提供一个在模态控制器完成时调用的块。

所以在VC2中,提供一个名为onDoneBlock的块属性。

在VC1中,您提供如下:

  • 在VC1中,创建VC2

  • 将VC2的done处理程序设置为: VC2.onDoneBlock={[VC2 dismissViewControllerAnimated:YES completion:nil]};

  • 使用[self presentViewController:VC2 animated:YES completion:nil]正常呈现VC2控制器;

  • 在VC2中,在取消目标操作中调用self.onDoneBlock();

结果是VC2告诉谁提出它已经完成。 您可以扩展onDoneBlock以具有参数,该参数指示模式是否已被选中,取消,成功等….

使用块属性

在VC2中声明

 var onDoneBlock : ((Bool) -> Void)? 

在VC1中安装

 VC2.onDoneBlock = { result in // Do something } 

当您即将解雇时,请在VC2中拨打电话

 onDoneBlock!(true) 

呈现呈现的视图控制器都可以调用dismissViewController:animated:以关闭呈现的视图控制器。

前一个选项(可以说)是“正确的”,设计明智的:相同的“父”视图控制器负责呈现和解除模态(“子”)视图控制器。

但是,后者更方便:通常,“dismiss”按钮附加到呈现的视图控制器的视图,并且它将所述视图控制器设置为其动作目标。

如果您采用前一种方法,则您已经知道呈现视图控制器中发生解雇的代码dismissViewControllerAnimated:completion:dismissViewControllerAnimated:completion:之后运行代码,或者在完成块内运行代码。

如果您采用后一种方法(呈现的视图控制器自行解除),请记住,从呈现的视图控制器调用dismissViewControllerAnimated:completion:会导致UIKit在呈现的视图控制器上调用该方法:

讨论

呈现视图控制器负责解除它所呈现的视图控制器。 如果您在呈现的视图控制器本身上调用此方法,UIKit会要求呈现视图控制器处理解雇。

( 来源:UIViewController类参考 )

因此,为了拦截此类事件,您可以在呈现视图控制器中覆盖该方法:

 override func dismissViewControllerAnimated(_ flag: Bool, completion completion: (() -> Void)?) { super.dismissViewControllerAnimated(flag, completion:completion) // Your custom code here... } 

您可以使用unwind segue来执行此任务,无需使用dismissModalViewController。 在VC1中定义展开segue方法。

请参阅此链接,了解如何创建展开segue, https: //stackoverflow.com/a/15839298/5647055。

假设您的unwind segue已设置,在为“取消”按钮定义的操作方法中,您可以执行以下操作:

 [self performSegueWithIdentifier:@"YourUnwindSegueName" sender:nil]; 

现在,每当您按下VC2中的“取消”按钮时,它将被解除,VC1将出现。 它还将调用您在VC1中定义的unwind方法。 现在,您知道所呈现的视图控制器何时被解除。

@ user523234 – “但VC1中的dismissViewControllerAnimated没有被调用。”

你不能假设VC1实际上做了呈现 – 它可能是根视图控制器,VC0。 涉及3个视图控制器:

  • sourceViewController
  • presentingViewController
  • presentedViewController

在你的例子中, VC1 = sourceViewControllerVC2 = presentedViewController VC1 = sourceViewController?? = presentingViewController ?? = presentingViewController – 也许是VC1,也许不是。

但是,在解除VC2时,你总是可以依赖VC1.animationControllerForDismissedController(如果你已经实现了委托方法),在那个方法中你可以用VC1做你想做的事情。

我使用以下内容向协调器发出视图控制器已“完成”的信号。 这用于tvOS应用程序中的AVPlayerViewController子类,并将在playerVC解雇转换完成后调用:

 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?() } }) } } 
  1. 创建一个类文件(.h / .m)并命名它:DismissSegue
  2. 选择子类:UIStoryboardSegue

  3. 转到DismissSegue.m文件并记下以下代码:

     - (void)perform { UIViewController *sourceViewController = self.sourceViewController; [sourceViewController.presentingViewController dismissViewControllerAnimated:YES completion:nil]; } 
  4. 打开故事板然后按Ctrl +从取消按钮拖动到VC1并选择Action Segue作为Dismiss,你就完成了。

如果在视图控制器上覆盖被覆盖:

 override func removeFromParentViewController() { super.removeFromParentViewController() // your code here } 

至少这对我有用。

override viewDidAppear为我做了伎俩。 我在我的模态中使用了Singleton ,现在我可以在调用VC,模态和其他任何地方设置和获取它。

在呈现的视图控制器中覆盖viewWillDisappear函数。

 override func viewWillDisappear(_ animated: Bool) { //Your code here } 

如前所述,解决方案是使用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) } }