检测表在 iOS13上被取消

在 iOS13之前,呈现的视图控制器用于覆盖整个屏幕。而且,当解散时,将执行父视图控制器 viewDidAppear函数。

现在 iOS 13将视图控制器作为默认表显示,这意味着卡将部分覆盖底层视图控制器,这意味着不会调用 viewDidAppear,因为父视图控制器实际上从未消失。

有没有一种方法来 检测所提供的视图控制器表被解散? 其他一些功能,我可以覆盖在父视图控制器 而不是使用某种委托

62145 次浏览

是否有一种方法可以检测出呈现的视图控制器表是否被忽略?

是的。

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

不。“某种类型的委托”是你如何做到这一点。使自己的表示控制器的委托和覆盖 presentationControllerDidDismiss(_:)

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


缺少一个通用的运行时生成的事件来告诉你一个呈现的视图控制器,不管是不是全屏的,已经被解除了,这确实是一个麻烦; 但这并不是一个新问题,因为总是有非全屏的呈现的视图控制器。只是现在(在 iOS13中)有更多这样的人!我在其他地方专门为这个主题提供了一个单独的问答: 统一 UIViewController“成为最前沿”的检测?

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

  1. 在根视图控制器准备继续添加以下内容(假设您的模式有一个导航控制器)
    // 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. 在模态 View Controller 中确保以下属性为 true,以便调用委托方法
    self.isModalInPresentation = true
  1. 利润

下面是一个父视图控制器的代码示例,当它呈现的子视图控制器 像床单一样(即,以默认的 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)当表是 解散时,最初的提问者希望得到一个函数调用(而他实现了 presationControllerDidAttemtToDismiss,当用户尝试 失败了解除表时,就会调用该函数) ,(b)设置 isModalInPresation 是完全正交的,实际上会使所呈现的表不可解除(这与 OP 想要的相反)。

设置了返回 viewWillAppearviewDidAppear的另一个选项

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

此选项覆盖全屏,并在解除后,调用上述方法

调用 PresingViewController.viewWillAppear 不是很简单吗? 才开除你?

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

在我看来,苹果不应该设置 pageSheet是默认的 modalPresentationStyle

我想使用 swizzling使 fullScreen风格回到默认状态

像这样:

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)在 root 视图控制器中,告诉哪个是它的表示视图控制器,如下面的代码所示

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

enter image description here

在被解雇的 UIViewController上重写 viewWillDisappear。它会通过 isBeingDismissed布尔标志提醒你解雇。

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


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

如果用户在向下滑动一半的时候又向上滑动卡片,即使卡片没有被注销,它仍然会被注册为被注销。但这是一个你可能不关心的边缘案件。

斯威夫特

IOS13中调用 viewWillAppear的通用解决方案

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)
}
}
}

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

ConsultarController 控制器 Consultar = this. Storyboard. InstantiateViewController (“ ConsultarController”)作为 ConsultarController; ModalPresentationStyle = UIModalPresentationStyle PushViewController (controller,Consultar,true) ;

如果有人没有访问所提供的视图控制器,他们可以只覆盖下面的方法,提出视图控制器,并改变 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
}

如果您希望在用户从该工作表中关闭模态工作表时执行某些操作。 假设您已经有了一些 Close 按钮,其中包含 @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
}
}

在 SwiftUI 中,您可以使用 onDismiss 闭包

func sheet<Item, Content>(item: Binding<Item?>, onDismiss: (() -> Void)?, content: (Item) -> Content) -> some View