148

在 iOS 13 之前,呈现的视图控制器用于覆盖整个屏幕。并且,当被解雇时,父视图控制器viewDidAppear功能被执行。

现在 iOS 13 将默认将视图控制器呈现为一张表,这意味着卡片将部分覆盖底层视图控制器,这意味着viewDidAppear不会被调用,因为父视图控制器从未真正消失过。

有没有办法检测到呈现的视图控制器表被解雇了?我可以在父视图控制器中覆盖其他一些功能,而不是使用某种委托

4

13 回答 13

79

有没有办法检测到呈现的视图控制器表被解雇了?

是的。

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

不,“某种代表”就是你的做法。让自己成为演示控制器的委托并覆盖presentationControllerDidDismiss(_:)

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


缺少一个通用的运行时生成的事件来通知您呈现的视图控制器(无论是否全屏)已被解除,这确实很麻烦;但这不是一个新问题,因为一直存在非全屏呈现的视图控制器。只是现在(在 iOS 13 中)还有更多!我在其他地方专门针对这个主题进行了单独的问答:Unified UIViewController“成为最前沿”检测?.

于 2019-06-12T19:21:04.053 回答
49

这是父视图控制器的代码示例,当它作为工作表呈现的子视图控制器(即,以默认的 iOS 13 方式)被关闭时,会收到通知:

public final class Parent: UIViewController, UIAdaptivePresentationControllerDelegate
{
  // This is assuming that the segue is a storyboard segue; 
  // if you're manually presenting, just set 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 想要的相反)。

于 2019-09-05T10:49:26.893 回答
29

另一个选择返回viewWillAppearviewDidAppear设置

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

此选项覆盖全屏,关闭后调用上述方法

于 2019-09-25T07:17:17.097 回答
24

对于未来的读者,这里有一个更完整的实施答案:

  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. 确保在模态视图控制器中以下属性为真,以便调用委托方法
    self.isModalInPresentation = true
  1. 利润
于 2019-08-28T17:55:12.063 回答
8

迅速

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)
            }
        }
    }
于 2020-04-09T18:47:33.773 回答
6

如果您想在用户从该工作表中关闭模式工作表时执行某些操作。假设您已经有一些关闭按钮,其中包含一个@IBAction和一个逻辑,用于在关闭或执行其他操作之前显示警报。您只想检测用户按下此类控制器的时刻。

就是这样:

class MyModalSheetViewController: UIViewController {

     override func viewDidLoad() {
        super.viewDidLoad()

        self.presentationController?.delegate = self
     }

     @IBAction func closeAction(_ sender: Any) {
         // your logic to decide to close or not, when to close, etc.
     }

}

extension MyModalSheetViewController: UIAdaptivePresentationControllerDelegate {

    func presentationControllerShouldDismiss(_ presentationController: UIPresentationController) -> Bool {
        return false // <-prevents the modal sheet from being closed
    }

    func presentationControllerDidAttemptToDismiss(_ presentationController: UIPresentationController) {
        closeAction(self) // <- called after the modal sheet was prevented from being closed and leads to your own logic
    }
}
于 2021-01-08T16:42:16.100 回答
6

覆盖正在被解雇viewWillDisappear的那个。它会通过布尔标志UIViewController提醒您解雇。isBeingDismissed

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

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

** 如果用户在向下滑动中途并向上刷卡,即使卡片没有被关闭,它仍然会注册为被关闭。但这是你可能不关心的边缘情况。

于 2020-03-30T23:56:07.887 回答
4

DRAG OR 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!)
    }

在此处输入图像描述

于 2020-02-02T13:26:39.933 回答
1

在 SwiftUI 中你可以使用 onDismiss 闭包

func sheet<Item, Content>(item: Binding<Item?>, onDismiss: (() -> Void)?, content: (Item) -> Content) -> some View
于 2021-05-02T10:43:50.517 回答
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
}
于 2020-07-17T09:09:43.673 回答
-2

如果您在 FullScreen 中使用 ModalPresentationStyle,则控制器的行为将恢复正常。

ConsultarController controllerConsultar = this.Storyboard.InstantiateViewController("ConsultarController") as ConsultarController; controllerConsultar.ModalPresentationStyle = UIModalPresentationStyle.FullScreen; this.NavigationController.PushViewController(controllerConsultar, true);

于 2020-04-21T01:18:46.743 回答
-3

从我的角度来看,Apple不应该设置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()
于 2019-10-24T02:21:39.207 回答
-7

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

self.presentingViewController?.viewWillAppear(false)
self.dismiss(animated: true, completion: nil)
于 2019-10-23T15:56:31.257 回答