一尘不染

ios-无论视图层次如何,都在所有内容之上显示UIAlertController

swift

我正在尝试提供一个帮助类,以呈现一个UIAlertController。由于它是一个帮助器类,因此我希望它能在不考虑视图层次结构的情况下正常工作,并且没有有关它的信息。我能够显示警报,但是当它被关闭时,该应用程序崩溃并显示:

*** 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因为这将取决于当前视图层次结构,而我希望弹出窗口是独立的

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

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

阅读 539

收藏
2020-07-07

共1个答案

一尘不染

2019年12月16日更新:

只需显示当前最顶层视图控制器中的视图控制器/警报即可。那可行 :)

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中停止工作 :(

我会在有时间调查时会更新…

旧技术:

这是它的Swift(5)扩展:

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,然后调用:

alert.show()

不再受View Controllers层次结构的约束!

2020-07-07