iOS 多个弹窗报错 [Presentation] Attempt to present <UIAlertController: 0x11105aa00> on

90 阅读1分钟

报错原因:

问题在于试图在一个 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()

           } 写代码就是这样,就像攻山头,一个山头一个山头的攻下来,一点心得,与诸位共勉