检测表已在iOS 13上关闭


113

在iOS 13之前,提供了用于覆盖整个屏幕的视图控制器。并且,在关闭后,将viewDidAppear执行父视图控制器功能。

现在,iOS 13默认将表单显示为视图控制器,这意味着卡将部分覆盖基础视图控制器,这意味着viewDidAppear不会被调用,因为父视图控制器从未真正消失过。

有没有一种方法可以检测到所显示的视图控制器工作表已被解雇?我可以在父视图控制器中重写某些其他功能,而不是使用某种委托



那么,有没有一种方法可以一次将所有的模态表释放给根vc?
Weslie


为什么您需要知道什么时候解雇它?如果要重新加载数据并更新UI,则Notifications或KVO可能是一个不错的选择。
martn_st

Answers:


57

有没有一种方法可以检测到所显示的视图控制器工作表已被解雇?

是。

我可以在父视图控制器中重写某些其他功能,而不是使用某种委托?

不,“某种委托”是您的工作方式。使自己成为演示控制器的委托和重写presentationControllerDidDismiss(_:)

https://developer.apple.com/documentation/uikit/uiadaptivepresentationcontrollerdelegate/3229889-presentationcontrollerdiddismiss


缺少一个一般的运行时生成的事件来通知您,所显示的视图控制器(无论是否为全屏)已被关闭,这确实很麻烦。但这不是一个新问题,因为始终存在非全屏显示的视图控制器。只是现在(在iOS 13中)有更多!我在其他地方对此主题进行了单独的问答:统一UIViewController“成为最前端”检测?


6
这还不够。如果您呈现的VC中有一个nabber,并且有一个以编程方式关闭视图的自定义条形按钮,则不会调用presentation controller的关闭。
伊琳娜

14
@Irina,您好-如果您以编程方式关闭视图,则不需要回调,因为您以编程方式关闭了视图-您知道是因为这样做了。委托方法仅在用户执行的情况下使用
马特

6
@matt感谢您的回答。当以编程方式关闭视图时,该视图不会被调用(如Irina所说),并且您是对的,我们知道我们做到了。我只是认为不需要编写过多的样板代码即可使用iOS13中的新模态表示样式来获得一种“ viewWillAppear”。当您通过提取了路由的体系结构来管理路由时(例如,在MVVM +协调器中,或者在VIPER中为路由器类型),它会变得特别混乱
Adam Waite,

3
@AdamWaite我同意,但是这个问题并不新鲜。多年来,我们遇到了这个问题,包括弹出窗口,非全屏显示视图控制器,警报等等。我认为这是苹果公司“事件”报道中的一个严重缺陷。我只是说现实是什么,为什么。我直接在这里解决问题:stackoverflow.com/questions/54602662/…–
马特

1
PresentationControllerDidDismiss(_ :)。在Child VC中单击“后退”按钮时未调用。有帮助吗?
克里希纳·梅纳

41

这是父视图控制器的代码示例,当其以表单形式显示(即以默认的iOS 13方式)时,通知该视图控制器:

public final class Parent: UIViewController, UIAdaptivePresentationControllerDelegate
{
  // This is assuming that the segue is a storyboard segue; 
  // if you're manually presenting, just see the delegate there.
  public override func prepare(for segue: UIStoryboardSegue, sender: Any?)
  {
    if segue.identifier == "mySegue" {
      segue.destination.presentationController?.delegate = self;
    }
  }

  public func presentationControllerDidDismiss(
    _ presentationController: UIPresentationController)
  {
    // Only called when the sheet is dismissed by DRAGGING.
    // You'll need something extra if you call .dismiss() on the child.
    // (I found that overriding dismiss in the child and calling
    // presentationController.delegate?.presentationControllerDidDismiss
    // works well).
  }
}

Jerland2的答案很混乱,因为(a)原始提问者想在关闭工作表时获得一个函数调用(而他实现了presentationControllerDidAttemptToDismiss,当用户尝试并无法关闭工作表时会调用它),并且(b)设置isModalInPresentation是完全正交的,实际上会使所呈现的图纸不可忽略(这与OP想要的相反)。


6
这很好。提示:如果在被调用的VC上使用导航控制器,则应将导航控制器分配为presentationController?,delegate(而不是将导航所具有的VC作为topViewController)。
instAustralia '19

@instAustralia您可以解释原因或参考文档吗?谢谢。
艾哈迈德·奥萨马

presentationControllerDidDismiss用户按下后退按钮时如何调用它?
克里希纳·梅纳

@AhmedOsama-导航控制器是演示控制器,因此是委托,因为它将是对撤消的响应。我也尝试过嵌入Nav Controller中的VC,但这是我要关闭的实际按钮存在并响应的地方。我无法在Apple文档中直接找到它,但在这里引用了sarunw.com/posts/modality-changes-in-ios13
instAustralia

27

另一个选择回来viewWillAppearviewDidAppear设置

let vc = UIViewController()
vc.modalPresentationStyle = .fullScreen

此选项全屏显示,关闭后调用上述方法


2
谢谢PiterPan。可以了 这是很好且最快的解决方案。
Erkam KUCET

感谢您使用这种快速可靠的方法来还原以前的默认行为。能够立即修复此问题,然后以合理的方式计划向新行为的过渡,真是太好了。
伊恩·洛夫乔伊

11
这是一种解决方法,而不是解决方法。所有人都回到iOS 12样式表并不是很好。iOS 13很棒!:)
马特

1
在iPad上使用时请务必小心,因为在默认情况下,iPad默认显示为pageSheet。这将迫使iPad呈现为
全屏

不适合我。我打开模态控制器。以关闭关闭它,但是willAppear没有被调用。为什么?谢谢
neo999

21

对于将来的读者,这里是实现的更完整答案:

  1. 在根视图控制器中为segue做准备,添加以下内容(假设您的模态具有导航控制器)
    // Modal Dismiss iOS 13
    modalNavController.presentationController?.delegate = modalVc
  1. 在模态视图控制器中,添加以下委托+方法
// MARK: - iOS 13 Modal (Swipe to Dismiss)

extension ModalViewController: UIAdaptivePresentationControllerDelegate {
    func presentationControllerDidAttemptToDismiss(_ presentationController: UIPresentationController) {


        print("slide to dismiss stopped")
        self.dismiss(animated: true, completion: nil)
    }
}
  1. 确保在模式视图控制器中,以下属性为true,以便调用委托方法
    self.isModalInPresentation = true
  1. 利润

1
self.isModalInPresentation = true,则拖动关闭不起作用。删除该行委托方法仍然称为okay。谢谢。
Yogesh Patel

2
这是令人困惑的,因为(a)原始提问者想在关闭工作表时获得一个函数调用(而您已经实现了presentationControllerDidAttemptToDismiss,当用户尝试并无法关闭工作表时会被调用),并且(b)设置isModalInPresentation是完全正交的,实际上会使所呈现的图纸不可忽略(这与OP想要的相反)。
马特

1
@Matt的答案(a)的后续操作:使用presentationControllerDidDismiss应该有效
gondo

5

迅速

一般解决呼叫 viewWillAppeariOS13

class ViewController: UIViewController {

        override func viewWillAppear(_ animated: Bool) {
            super.viewWillAppear(animated)
            print("viewWillAppear")
        }

        //Show new viewController
        @IBAction func show(_ sender: Any) {
            let newViewController = NewViewController()
            //set delegate of UIAdaptivePresentationControllerDelegate to self
            newViewController.presentationController?.delegate = self
            present(newViewController, animated: true, completion: nil)
        }
    }

    extension UIViewController: UIAdaptivePresentationControllerDelegate {
        public func presentationControllerDidDismiss( _ presentationController: UIPresentationController) {
            if #available(iOS 13, *) {
                //Call viewWillAppear only in iOS 13
                viewWillAppear(true)
            }
        }
    }

1
这只能使用顶部的幻灯片来处理撤消,而不是通过调用函数来处理dismiss(_)
Pedro Paulo Amorim

3

DRAG或CALL DISMISS FUNC将与以下代码一起使用。

1)在根视图控制器中,您知道哪个是其演示视图控制器,如下代码

 override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "presenterID" {
        let navigationController = segue.destination as! UINavigationController
        if #available(iOS 13.0, *) {
            let controller = navigationController.topViewController as! presentationviewcontroller
            // Modal Dismiss iOS 13
            controller.presentationController?.delegate = self
        } else {
            // Fallback on earlier versions
        }
        navigationController.presentationController?.delegate = self

    }
}

2)再次在根视图控制器中,告诉您当演示文稿视图控制器消失时将要执行的操作

public func presentationControllerDidDismiss(
  _ presentationController: UIPresentationController)
{
    print("presentationControllerDidDismiss")
}

1)在演示视图控制器中,单击此图片中的“取消”或“保存”按钮。下面的代码将被调用。

self.dismiss(animated: true) {
        self.presentationController?.delegate?.presentationControllerDidDismiss?(self.presentationController!)
    }

在此处输入图片说明


1
是否有必要将navigationController.topViewController转换为presentationViewController?我发现不是
Fitsyu

从取消按钮子VC退出后,如何在父VC中重新加载数据?
克里希纳·梅纳

3

覆盖正在被关闭的UIViewController上的viewWillDisappear。它将通过isBeingDismissed布尔标志提醒您被解雇。

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)

    if isBeingDismissed {
        print("user is dismissing the vc")
    }
}

**如果用户在中途向下滑动并向后滑动卡时,即使卡未被取消,该卡仍会注册为已被取消。但这是一个极端情况,您可能并不在意。


怎么样self.dismiss(animated: Bool, completion: (() -> Void)?)
iGhost

0

如果某人无权访问呈现的视图控制器,则他们可以在呈现视图控制器时重写以下方法,并将其更改modalPresentationStylefullScreen或可以使用此方法添加上述策略之一

 override func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
    if let _ = viewControllerToPresent as? TargetVC {
        viewControllerToPresent.modalPresentationStyle = .fullScreen
    }
    super.present(viewControllerToPresent, animated: flag, completion: completion)
}

如果显示的视图控制器是导航控制器,并且您要检查根控制器,则可以将上述条件更改为

if let _ = (viewControllerToPresent as? UINavigationController)?.viewControllers.first as? TargetVC {
   viewControllerToPresent.modalPresentationStyle = .fullScreen
}

-2

如果在FullScreen中使用了ModalPresentationStyle,则控制器的行为将恢复为通常的状态。

ConsultarController controllerConsultar = this.Storyboard.InstantiateViewController(“ ConsultarController”)作为ConsultarController; controllerConsultar.ModalPresentationStyle = UIModalPresentationStyle.FullScreen; this.NavigationController.PushViewController(controllerConsultar,true);


重复现有答案。
马特

-3

从我的角度来看,苹果不应该设置pageSheet默认modalPresentationStyle

我想fullScreen通过使用将样式恢复为默认值swizzling

像这样:

private func _swizzling(forClass: AnyClass, originalSelector: Selector, swizzledSelector: Selector) {
    if let originalMethod = class_getInstanceMethod(forClass, originalSelector),
       let swizzledMethod = class_getInstanceMethod(forClass, swizzledSelector) {
        method_exchangeImplementations(originalMethod, swizzledMethod)
    }
}

extension UIViewController {

    static func preventPageSheetPresentationStyle () {
        UIViewController.preventPageSheetPresentation
    }

    static let preventPageSheetPresentation: Void = {
        if #available(iOS 13, *) {
            _swizzling(forClass: UIViewController.self,
                       originalSelector: #selector(present(_: animated: completion:)),
                       swizzledSelector: #selector(_swizzledPresent(_: animated: completion:)))
        }
    }()

    @available(iOS 13.0, *)
    private func _swizzledPresent(_ viewControllerToPresent: UIViewController,
                                        animated flag: Bool,
                                        completion: (() -> Void)? = nil) {
        if viewControllerToPresent.modalPresentationStyle == .pageSheet
                   || viewControllerToPresent.modalPresentationStyle == .automatic {
            viewControllerToPresent.modalPresentationStyle = .fullScreen
        }
        _swizzledPresent(viewControllerToPresent, animated: flag, completion: completion)
    }
}

然后把这条线放到你的 AppDelegate

UIViewController.preventPageSheetPresentationStyle()

1
这很巧妙,但我不同意。它很hacky,更重要的是,它与iOS 13背道而驰。您应该在iOS 13中使用“卡片式”演示文稿。Apple期望我们做出的回应不是“解决”。它是“克服它”。
马特

同意您的观点,此解决方案无助于使用Apple鼓励我们使用的卡片展示方式。但是,将其设置为默认样式将使现有代码行在某处出错,因为它presentingViewController不会触发viewWillAppear
jacob

1
是的,但是正如我在我自己的回答中已经说过的那样,对于非全屏演示文稿(例如iPad上的弹出窗口和页面/表单)来说,这始终是一个问题,因此这并不是什么新鲜事物。只是现在有了更多。viewWillAppear某种意义上说,依靠总是错误的。当然,我不喜欢Apple来跟从我下面挖开地板。但是正如我所说,我们只需要忍受这一点,并以新的方式做事。
马特

在我的项目中,有些情况下我不知道在何处显示视图控制器(称为presentedController),并且都不知道确切是什么presentingViewController。例如:在某些情况下,我必须使用UIViewController.topMostViewController()它使我返回当前窗口中最顶级的视图控制器。因此,为什么我想做一些努力以保持当前行为在viewWillAppear我的视图控制器中做正确的事情(刷新数据,UI)。如果您有解决此问题的想法,请提供帮助。
雅各布

好吧,我相信,我在答案结尾处链接的解决方案确实可以解决该问题。在演示时进行配置需要花费一些工作,但是基本上可以确保每个演示者(包括警报的演示者)在关闭所显示的视图控制器时都能听到声音。
马特

-5

调用presentingViewController.viewWillAppear不是很简单吗?被解雇?

self.presentingViewController?.viewWillAppear(false)
self.dismiss(animated: true, completion: nil)

4
不是您的电话。
马特
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.