检测表在 iOS 13 上被取消

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/56568967/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 09:55:25  来源:igfitidea点击:

Detecting sheet was dismissed on iOS 13

iosswiftuiviewcontrolleruikitios13

提问by Marcos Tanaka

Before iOS 13, presented view controllers used to cover the entire screen. And, when dismissed, the parent view controller viewDidAppearfunction were executed.

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

Now iOS 13 will present view controllers as a sheet as default, which means the card will partially cover the underlying view controller, which means that viewDidAppearwill not be called, because the parent view controller has never actually disappeared.

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

Is there a way to detect that the presented view controller sheet was dismissed? Some other function I can override in the parent view controller rather than using some sort of delegate?

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

采纳答案by matt

Is there a way to detect that the presented view controller sheet was dismissed?

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

Yes.

是的。

Some other function I can override in the parent view controller rather than using some sort of delegate?

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

No. "Some sort of delegate" is how you do it. Make yourself the presentation controller's delegate and override presentationControllerDidDismiss(_:).

不。“某种委托”是你如何做的。使自己成为演示控制器的委托并覆盖presentationControllerDidDismiss(_:)

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

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



The lack of a general runtime-generated event informing you that a presented view controller, whether fullscreen or not, has been dismissed, is indeed troublesome; but it's not a new issue, because there have always been non-fullscreen presented view controllers. It's just that now (in iOS 13) there are more of them! I devote a separate question-and-answer to this topic elsewhere: Unified UIViewController "became frontmost" detection?.

缺少一个通用的运行时生成的事件来通知您所呈现的视图控制器(无论是否全屏)已被取消,这确实很麻烦;但这不是一个新问题,因为一直存在非全屏呈现的视图控制器。只是现在(在 iOS 13 中)有更多它们!我在别处专门针对此主题进行了单独的问答:Unified UIViewController “become frontmost” detection?.

回答by Matt

Here's a code example of a parent view-controller which is notified when the child view-controller it presents as a sheet(i.e., in the default iOS 13 manner) is dismissed:

这是父视图控制器的代码示例,当它作为工作表呈现的子视图控制器(即,以默认的 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's answer is confused, since (a) the original questioner wanted to get a function call when the sheet is dismissed(whereas he implemented presentationControllerDidAttemptToDismiss, which is called when the user tries and failsto dismiss the sheet), and (b) setting isModalInPresentation is entirely orthogonal and in fact will make the presented sheet undismissable (which is the opposite of what OP wants).

Jerland2 的回答令人困惑,因为 (a) 原始提问者希望在关闭工作表时调用函数(而他实现了presentationControllerDidAttemptToDismiss,当用户尝试关闭工作表失败时调用),以及 (b) 设置 isModalInPresentation是完全正交的,实际上将使所呈现的工作表不可拒绝(这与 OP 想要的相反)。

回答by PiterPan

Another option to get back viewWillAppearand viewDidAppearis set

返回viewWillAppearviewDidAppear设置的另一个选项

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

this option cover full screen and after dismiss, calls above methods

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

回答by Jerland2

For future readers here is a more complete answer with implementation:

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

  1. In the root view controllers prepare for segue add the following (Assuming your modal has a nav controller)
  1. 在准备转场的根视图控制器中添加以下内容(假设您的模式有一个导航控制器)
    // Modal Dismiss iOS 13
    modalNavController.presentationController?.delegate = modalVc
  1. In the modal view controller add the following delegate + method
  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. Ensure in the modal View Controller that the following property is true in order for the delegate method to be called
  1. 确保在模态视图控制器中以下属性为真,以便调用委托方法
    self.isModalInPresentation = true
  1. Profit
  1. 利润

回答by coders

DRAG OR CALL DISMISS FUNC will work with below code.

DRAG OR CALL DISMISS FUNC 将使用以下代码。

1) In root view controller, you tell that which is its presentation view controller as below code

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) Again in the root view controller, you tell what you will do when its presentation view controller is dissmised

2) 再次在根视图控制器中,你告诉当它的呈现视图控制器被关闭时你会做什么

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

1) In the presentation view controller, When you hit cancel or save button in this picture. Below code will be called.The

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

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

enter image description here

在此处输入图片说明

回答by craft

Override viewWillDisappear on the UIViewController that's being dismissed. It will alert you to a dismissal via isBeingDismissedboolean flag.

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

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

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

** If the user is halfway through the swipe down and swipes the card back up, it'll still register as being dismissed, even if the card is not dismissed. But that's an edge case you may not care about.

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

回答by dimo hamdy

Swift

迅速

General Solution to callviewWillAppearin iOS13

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

回答by Abelardo del angel Quiroz

If you used the ModalPresentationStyle in FullScreen, the behavior of the controller is back as usual.

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

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

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

回答by jacob

From my point of view, Apple should not set pageSheetis the default modalPresentationStyle

在我看来,Apple 不应该设置pageSheet为默认值modalPresentationStyle

I'd like to bring fullScreenstyle back to default by using swizzling

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

Like this:

像这样:

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

And then put this line to your AppDelegate

然后把这条线放到你的 AppDelegate

UIViewController.preventPageSheetPresentationStyle()

回答by Mikesch8764

wouldn't it be simple to call the presentingViewController.viewWillAppear? befor dismissing?

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

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