You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift UIKit:如何无需嵌套按钮动作有序依次显示多个Alert

有序显示多个UIAlertController的更优方案

在Swift中直接连续调用present(_:animated:completion:)展示多个弹窗会失效,因为UIKit要求必须等当前弹窗完全dismiss后才能展示下一个。要避免把弹窗逻辑嵌套在按钮闭包里,你可以试试以下两种更清晰的实现方式:

方法一:弹窗队列管理器

创建一个全局的弹窗管理类,用队列存储待展示的弹窗,自动依次展示,无需手动处理顺序:

class AlertManager {
    static let shared = AlertManager()
    private var alertQueue: [UIAlertController] = []
    private var isPresenting = false
    
    // 将弹窗加入队列,自动触发展示
    func enqueueAlert(_ alert: UIAlertController, from viewController: UIViewController) {
        alertQueue.append(alert)
        if !isPresenting {
            presentNextAlert(from: viewController)
        }
    }
    
    // 展示队列中的下一个弹窗
    private func presentNextAlert(from viewController: UIViewController) {
        guard !alertQueue.isEmpty else {
            isPresenting = false
            return
        }
        isPresenting = true
        let nextAlert = alertQueue.removeFirst()
        
        // 重写弹窗按钮的回调,确保弹窗关闭后自动展示下一个
        let originalActions = nextAlert.actions
        nextAlert.removeAllActions()
        for action in originalActions {
            let newAction = UIAlertAction(title: action.title, style: action.style) { [weak self] _ in
                action.handler?() // 执行原按钮逻辑
                self?.presentNextAlert(from: viewController) // 触发下一个弹窗
            }
            nextAlert.addAction(newAction)
        }
        
        viewController.present(nextAlert, animated: true)
    }
}

使用方式

在你的视图控制器中,只需把要展示的弹窗加入队列即可:

override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    let person = people[indexPath.item]
    
    // 创建第一个弹窗
    let questionAc = UIAlertController(title: "What is your purpose?", message: "", preferredStyle: .alert)
    let deleteButton = UIAlertAction(title: "Delete", style: .destructive) { [weak self] _ in
        self?.people.remove(at: indexPath.item)
        collectionView.reloadData()
    }
    let renameButton = UIAlertAction(title: "Rename", style: .default)
    questionAc.addAction(deleteButton)
    questionAc.addAction(renameButton)
    
    // 创建第二个弹窗
    let renameAc = UIAlertController(title: "Rename person", message: nil, preferredStyle: .alert)
    renameAc.addTextField()
    renameAc.addAction(UIAlertAction(title: "Cancel", style: .cancel))
    renameAc.addAction(UIAlertAction(title: "OK", style: .default, handler: { [weak self, weak renameAc] _ in
        guard let newName = renameAc?.textFields?[0].text else {return}
        person.name = newName
        self?.collectionView.reloadData()
    }))
    
    // 将弹窗加入队列,自动依次展示
    AlertManager.shared.enqueueAlert(questionAc, from: self)
    AlertManager.shared.enqueueAlert(renameAc, from: self)
}

方法二:用Completion Handler拆分弹窗逻辑

把每个弹窗的展示封装成独立方法,通过回调函数控制后续弹窗的展示,让代码结构更清晰:

extension UIViewController {
    // 封装弹窗展示,添加完成回调
    private func presentAlert(_ alert: UIAlertController, completion: (() -> Void)? = nil) {
        present(alert, animated: true) {
            completion?()
        }
    }
    
    // 展示操作选择弹窗
    func showActionSelectionAlert(for person: Person, indexPath: IndexPath, collectionView: UICollectionView, onComplete: @escaping () -> Void) {
        let questionAc = UIAlertController(title: "What is your purpose?", message: "", preferredStyle: .alert)
        let deleteButton = UIAlertAction(title: "Delete", style: .destructive) { [weak self] _ in
            self?.people.remove(at: indexPath.item)
            collectionView.reloadData()
            onComplete()
        }
        let renameButton = UIAlertAction(title: "Rename", style: .default) { _ in
            onComplete()
        }
        questionAc.addAction(deleteButton)
        questionAc.addAction(renameButton)
        presentAlert(questionAc)
    }
    
    // 展示重命名弹窗
    func showRenameAlert(for person: Person, collectionView: UICollectionView) {
        let renameAc = UIAlertController(title: "Rename person", message: nil, preferredStyle: .alert)
        renameAc.addTextField()
        renameAc.addAction(UIAlertAction(title: "Cancel", style: .cancel))
        renameAc.addAction(UIAlertAction(title: "OK", style: .default, handler: { [weak self, weak renameAc] _ in
            guard let newName = renameAc?.textFields?[0].text else {return}
            person.name = newName
            self?.collectionView.reloadData()
        }))
        presentAlert(renameAc)
    }
}

使用方式

在didSelectItemAt中链式调用封装好的方法:

override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    let person = people[indexPath.item]
    showActionSelectionAlert(for: person, indexPath: indexPath, collectionView: collectionView) { [weak self] in
        self?.showRenameAlert(for: person, collectionView: collectionView)
    }
}

这两种方法都能避免弹窗逻辑的嵌套,让代码更易维护和扩展。队列管理器适合处理不确定数量的弹窗排队,而Completion Handler方式更适合明确的顺序流程。

内容的提问来源于stack exchange,提问作者Mehmet S.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 10:45:45