ios - 无论视图层次如何,都将 UIAlertController 呈现在所有内容之上 [英] ios - present UIAlertController on top of everything regardless of the view hierarchy

查看:20
本文介绍了ios - 无论视图层次如何,都将 UIAlertController 呈现在所有内容之上的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试创建一个显示 UIAlertController 的帮助程序类.因为它是一个辅助类,所以我希望它不管视图层次结构如何都能工作,并且没有关于它的信息.我可以显示警报,但是当它被解除时,应用程序崩溃了:

I'm trying to have an helper class that presents an UIAlertController. Since it's a helper class, I want it to work regardless of the view hierarchy, and with no information about it. I'm able to show the alert, but when it's being dismissed, the app crashed with:

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException',
reason: 'Trying to dismiss UIAlertController <UIAlertController: 0x135d70d80>
 with unknown presenter.'

我正在创建弹出窗口:

guard let window = UIApplication.shared.keyWindow else { return }
let view = UIView()
view.isUserInteractionEnabled = true
window.insertSubview(view, at: 0)
window.bringSubview(toFront: view)
// add full screen constraints to view ...

let controller = UIAlertController(
  title: "confirm deletion?",
  message: ":)",
  preferredStyle: .alert
)

let deleteAction = UIAlertAction(
  title: "yes",
  style: .destructive,
  handler: { _ in
    DispatchQueue.main.async {
      view.removeFromSuperview()
      completion()
    }
  }
)
controller.addAction(deleteAction)

view.insertSubview(controller.view, at: 0)
view.bringSubview(toFront: controller.view)
// add centering constraints to controller.view ...

当我点击 yes 时,应用程序将崩溃,并且在崩溃之前处理程序没有被命中.我无法呈现 UIAlertController 因为这将取决于当前视图层次结构,而我希望弹出窗口是独立的

When I tap yes, the app will crash and the handler is not being hit before the crash. I can't present the UIAlertController because this would be dependent of the current view hierarchy, while I want the popup to be independant

快速解决方案感谢@Vlad 的想法.似乎在单独的窗口中操作要简单得多.所以这是一个有效的 Swift 解决方案:

Swift solution Thanks @Vlad for the idea. It seems that operating in a separate window is much more simple. So here is a working Swift solution:

class Popup {
  private var alertWindow: UIWindow
  static var shared = Popup()

  init() {
    alertWindow = UIWindow(frame: UIScreen.main.bounds)
    alertWindow.rootViewController = UIViewController()
    alertWindow.windowLevel = UIWindowLevelAlert + 1
    alertWindow.makeKeyAndVisible()
    alertWindow.isHidden = true
  }

  private func show(completion: @escaping ((Bool) -> Void)) {
    let controller = UIAlertController(
      title: "Want to do it?",
      message: "message",
      preferredStyle: .alert
    )

    let yesAction = UIAlertAction(
      title: "Yes",
      style: .default,
      handler: { _ in
        DispatchQueue.main.async {
          self.alertWindow.isHidden = true
          completion(true)
        }
    })

    let noAction = UIAlertAction(
      title: "Not now",
      style: .destructive,
      handler: { _ in
        DispatchQueue.main.async {
          self.alertWindow.isHidden = true
          completion(false)
        }
    })

    controller.addAction(noAction)
    controller.addAction(yesAction)
    self.alertWindow.isHidden = false
    alertWindow.rootViewController?.present(controller, animated: false)
  }
}

推荐答案

2019 年 12 月 16 日更新:

只需从当前最顶层的视图控制器中显示视图控制器/警报.这会工作:)

Just present the view controller/alert from the current top-most view controller. That will work :)

if #available(iOS 13.0, *) {
     if var topController = UIApplication.shared.keyWindow?.rootViewController  {
           while let presentedViewController = topController.presentedViewController {
                 topController = presentedViewController
                }
     topController.present(self, animated: true, completion: nil)
}

2019 年 7 月 23 日更新:

重要

显然,这种技术下面的方法在 iOS 13.0 中停止工作 :(

Apparently the method below this technique stopped working in iOS 13.0 :(

我会在有时间调查后更新...

I'll update once I find the time to investigate...

旧技术:

这是它的 Swift (5) 扩展:

Here's a Swift (5) extension for it:

public extension UIAlertController {
    func show() {
        let win = UIWindow(frame: UIScreen.main.bounds)
        let vc = UIViewController()
        vc.view.backgroundColor = .clear
        win.rootViewController = vc
        win.windowLevel = UIWindow.Level.alert + 1  // Swift 3-4: UIWindowLevelAlert + 1
        win.makeKeyAndVisible()    
        vc.present(self, animated: true, completion: nil)
    }
}

只需设置您的 UIAlertController,然后调用:

Just setup your UIAlertController, and then call:

alert.show()

不再受视图控制器层次结构的约束!

No more bound by the View Controllers hierarchy!

这篇关于ios - 无论视图层次如何,都将 UIAlertController 呈现在所有内容之上的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆