检测当前视图控制器被解除时

比方说,我有一个名为 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没有被叫到。

131450 次浏览
  1. 创建一个类文件(. h/. m)并将其命名为: DismissSegue
  2. 选择子类: UIStoryboardSegue

  3. 进入 DismissSegue.m 文件并写下以下代码:

    - (void)perform {
    UIViewController *sourceViewController = self.sourceViewController;
    [sourceViewController.presentingViewController dismissViewControllerAnimated:YES completion:nil];
    }
    
  4. Open storyboard & then Ctrl+drag from cancel button to VC1 & select Action Segue as Dismiss and you are done.

提出的 还有视图控制器都可以调用 dismissViewController:animated:来解除所提出的视图控制器。

前一个选项(可以说)在设计上是“正确的”: 同一个“父”视图控制器负责显示和取消模态(“子”)视图控制器。

然而,后者更为方便: 通常,“解散”按钮附加到所呈现的视图控制器的视图,并且它将视图控制器设置为其操作目标。

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

如果您采用后一种方法(提供的视图控制器会自动解散) ,请记住,从提供的视图控制器调用 dismissViewControllerAnimated:completion:会导致 UIKit 依次在提供的视图控制器上调用该方法:

讨论

呈现的视图控制器负责 忽略它提供的视图控制器。如果调用此方法 在呈现的视图控制器本身上,UIKit 要求呈现 视图控制器处理解雇。

(源代码: UIViewController 类引用)

因此,为了拦截这样的事件,您可以在 展示视图控制器中重写该方法:

override func dismiss(animated flag: Bool,
completion: (() -> Void)?) {
super.dismiss(animated: flag, completion: completion)


// Your custom code here...
}

根据文件,提交控制器负责实际解雇。当呈现的控制器自行解散时,它会要求呈现者为它做这件事。所以如果你在你的 VC1控制器中覆盖了 disseViewControllerAnimated,我相信当你在 VC2上点击取消时它会被调用。检测解雇,然后调用超级类版本,这将做实际的解雇。

正如从讨论中发现的那样,这似乎行不通。与其依赖底层机制,不如在 VC2本身上调用 dismissViewControllerAnimated:completion,在 VC2中调用 dismissViewControllerAnimated:completion。这将直接调用您的覆盖。

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

因此,在 VC2中,提供一个名为 onDoneBlock的块属性 say。

在 VC1中,你的介绍如下:

  • 在 VC1中,创建 VC2

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

  • 正常显示 VC2控制器,使用[ self presViewController: VC2 Animated: YES complete: nil ] ;

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

结果是 VC2告诉任何举起它的人它已经完成了。您可以扩展 onDoneBlock,使其具有指示模式是否已完成、取消、成功等的参数。

您可以使用 unwind segue 来完成这项任务,不需要使用 distsModalViewController。在 VC1中定义一个解除继续方法。

看看这个关于如何创建放松转折点的链接,https://stackoverflow.com/a/15839298/5647055

假设已经设置了放松继续,在为“取消”按钮定义的操作方法中,您可以按如下方式执行继续

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

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

@ user523234-“但是 VC1中被解雇的 ViewControllerAnimated 没有接到电话。”

您不能假设 VC1实际上进行了展示——它可能是根视图控制器,比如 VC0。涉及到3个视图控制器:

  • 来源视图控制器
  • 呈现 ViewController
  • 呈现的 ViewController

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

然而,在解除 VC2时,您总是可以依赖 VC1.animationControllerForDismisdedController 被调用(如果您已经实现了委托方法) ,并且在该方法中您可以对 VC1做您想做的事情

如果重写被解除的视图控制器:

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

至少这对我有用。

使用块属性

在 VC2中声明

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

VC1中的设置

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

准备解散的时候打电话给 VC2

onDoneBlock!(true)

我使用以下信号向协调器发出视图控制器已“完成”的信号。这是在 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?()
}
})
}
}

override2帮了我大忙。我在我的模式中使用了 辛格尔顿,现在我可以在调用 VC、模式和其他任何地方设置和获取它。

如前所述,解决方案是使用 override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil)

对于那些想知道为什么 override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil)似乎不总是工作,您可能会发现,呼叫被拦截的 UINavigationController,如果它是管理。我写了一个子类,应该会有帮助:

类 DismissingNavigationController: UINavigationController { 覆盖函数解散(动画标志: Bool,完成: (()-> Void) ? = nil){ Release (动画: 标志,完成: 完成) TopViewController? 解散(动画: 标志,完成: 完成) } }

在处理这个问题时,我已经看过这篇文章很多次了,我想我可能最终会给出一个可能的答案。

如果你需要知道用户发起的动作(比如屏幕上的手势)是否会对 UIActionController产生影响,并且不想花时间在创建子类或者扩展或者其他任何代码上,那么还有一个替代方案。

事实证明,UIActionController(或者说是任何 UIViewController)的 控制器属性有一个 授权,可以在代码中随时设置,类型为 代表,具有以下方法:

从您的操作控制器中分配委托,在委托类(视图、视图控制器或其他)中实现您选择的方法,然后瞧!

希望这个能帮上忙。

以下方式使用 willMove(toParent: UIViewController?)似乎对我有用。

override func willMove(toParent parent: UIViewController?) {
super.willMove(toParent: parent);


if parent == nil
{
// View controller is being removed.
// Perform onDismiss action
}
}

UIViewController中有一个称为 isBeingDismissed的特殊布尔属性,可以用来实现以下目的:

override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if isBeingDismissed {
// TODO: Do your stuff here.
}
}

如果要处理视图控制器解除的问题,应使用下面的代码。

- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
if (self.isBeingDismissed && self.completion != NULL) {
self.completion();
}
}

不幸的是,我们不能在重写的方法 -(void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^ _Nullable)(void))completion;中调用完成,因为只有在调用这个视图控制器的 release 方法时才会调用这个方法。

另一个选项是侦听自定义 UIPresentationController 的终止转换 DidEnd ()

extension Foo: UIAdaptivePresentationControllerDelegate {
func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
//call whatever you want
}
}


vc.presentationController?.delegate = foo

我使用了 Deinit作为 ViewController

deinit {
dataSource.stopUpdates()
}

在释放类实例之前立即调用反初始化器。

我没有看到什么似乎是一个简单的答案。原谅我,如果这是一个重复..。

由于 VC1负责解散 VC2,那么您需要在某个时候调用 VC1.release ()。因此,您可以在 VC1中覆盖 release () ,并在其中输入操作代码:

class VC1 : UIViewController {
override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
super.dismiss(animated: flag, completion: completion)
// PLACE YOUR ACTION CODE HERE
}
}

编辑: 您可能希望在解散完成时触发代码,而不是在开始时触发。所以在这种情况下,你应该使用:

    override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
super.dismiss(animated: flag) {
if let unwrapCompletion = completion { unwrapCompletion() }
// PLACE YOUR ACTION HERE
}
}

更有效的方法是创建一个用于呈现 Controller 的协议,然后调用 children Controller

protocol DismissListener {
    

func childControllerWillDismiss(_ controller : UIViewController,  animated : Bool)
func childControllerDidDismiss(_ controller : UIViewController,  animated : Bool)
}


extension UIViewController {
    

func dismissWithListener(animated flag: Bool, completion: (() -> Void)? = nil){
        

self.viewWillDismiss(flag)
self.dismiss(animated: flag, completion: {
completion?()
self.viewDidDismiss(true)
})
}
    

func viewWillDismiss(_ animate : Bool) {
(presentingViewController as? DismissListener)?.childControllerWillDismiss(self, animated: animate)
}
    

func viewDidDismiss(_ animate : Bool) {
(presentingViewController as? DismissListener)?.childControllerDidDismiss(self, animated: animate)
}
}

然后,当这种观点即将消失时:

self.dismissWithListener(animated: true, completion: nil)

最后只需要向任何你想要监听的 viewController 添加协议!

class ViewController: UIViewController, DismissListener {


func childControllerWillDismiss(_ controller: UIViewController, animated: Bool) {
}
    

func childControllerDidDismiss(_ controller: UIViewController, animated: Bool) {
}
}

你可以在 父视图控制器父视图控制器上使用 UIViewControllerTransitioningDelegate来观察另一个视图控制器的解除:

anotherViewControllerYouWantToObserve.transitioningDelegate = self

并观察解雇:

func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
print("anotherViewControllerYouWantToObserve was dismissed")
return nil
}

如果您有一个模态演示文稿,可以像页面工作表一样通过滑动来删除它,那么这个工作方法很有效。

override func endAppearanceTransition() {
if isBeingDismissed{
print("dismissal logic here")
}
}