检测呈现的视图控制器何时被解除
假设我有一个名为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 = sourceViewController
, VC2 = 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?() } }) } }
- 创建一个类文件(.h / .m)并命名它:DismissSegue
-
选择子类:UIStoryboardSegue
-
转到DismissSegue.m文件并记下以下代码:
- (void)perform { UIViewController *sourceViewController = self.sourceViewController; [sourceViewController.presentingViewController dismissViewControllerAnimated:YES completion:nil]; }
-
打开故事板然后按Ctrl +从取消按钮拖动到VC1并选择Action Segue作为Dismiss,你就完成了。
如果在视图控制器上覆盖被覆盖:
override func removeFromParentViewController() { super.removeFromParentViewController() // your code here }
至少这对我有用。
您可以使用Unwind Segues处理关闭的uiviewcontroller。
https://developer.apple.com/library/content/technotes/tn2298/_index.html
https://spin.atomicobject.com/2014/12/01/program-ios-unwind-segue/
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) } }