报错原因:
问题在于试图在一个 UIAlertController(已经显示的弹窗)上再展示另一个 UIAlertController。这在 iOS 中是不允许的,因为 UIAlertController 不能作为其他视图控制器的 presenter。
为了确保你的 AlertQueueManager 正确工作,并避免出现试图在非窗口层级中的视图控制器上展示新视图控制器的问题,我们需要确保每次只在最顶层的有效视图控制器上展示新的 UIAlertController
解决方案:增加弹窗队列管理 具体代码如下 class AlertQueueManager: NSObject {
static let shared = AlertQueueManager()
private var alertQueue: [(UIViewController) -> Void] = []
private var isShowing = false
private override init() {}
func enqueue(alertBuilder: @escaping (UIViewController) -> Void) {
DispatchQueue.main.async {
self.alertQueue.append(alertBuilder)
self.processNext()
}
}
private func processNext() {
guard !isShowing, let keyWindow = getKeyWindow(), let rootVC = keyWindow.rootViewController, !alertQueue.isEmpty else { return }
isShowing = true
let alertBuilder = alertQueue.removeFirst()
alertBuilder(rootVC)
}
func dequeue() {isShowing = false
processNext()
}
private var topViewController: UIViewController? {
guard var top = getKeyWindow()?.rootViewController else { return nil }
while let presented = top.presentedViewController {
top = presented
}
return top
}
}
// 获取当前活跃窗口(iOS 13+ 全兼容)
func getKeyWindow() -> UIWindow? {
if #available(iOS 15.0, *) {
return UIApplication.shared.connectedScenes
.filter { $0.activationState == .foregroundActive }
.compactMap { $0 as? UIWindowScene }
.first?.windows
.first { $0.isKeyWindow }
} else if #available(iOS 13.0, *) {
return UIApplication.shared.windows.first { $0.isKeyWindow }
}else{
return UIApplication.shared.keyWindow
}
}
如何使用 // 创建并显示进度弹窗
let alert = UIAlertController(title: downloadTileInfo, message: "0%", preferredStyle: .alert)
let heightConstraint = NSLayoutConstraint(item: alert.view!, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 0, constant: 200)
alert.view.addConstraint(heightConstraint)
// 添加进度条
let progressBar = UIProgressView(progressViewStyle: .default)
progressBar.translatesAutoresizingMaskIntoConstraints = false
alert.view.addSubview(progressBar)
progressBar.centerXAnchor.constraint(equalTo: alert.view.centerXAnchor).isActive = true
progressBar.centerYAnchor.constraint(equalTo: alert.view.centerYAnchor).isActive = true
progressBar.widthAnchor.constraint(equalToConstant: 200).isActive = true
let cancelAction = UIAlertAction(title: "取消", style: .cancel){[weak self] _ in
self?.cancelDownload()
}
alert.addAction(cancelAction)
// 入队
AlertQueueManager.shared.enqueue { viewController in
viewController.present(alert, animated: true) {
}
}
// 取消的地方
DispatchQueue.main.async {
alert.dismiss(animated: true)
AlertQueueManager.shared.dequeue()
} 写代码就是这样,就像攻山头,一个山头一个山头的攻下来,一点心得,与诸位共勉