ios - 无论视图层次如何,都将 UIAlertController 呈现在所有内容之上 [英] ios - present UIAlertController on top of everything regardless of the view hierarchy
问题描述
我正在尝试创建一个显示 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屋!