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.
相关产品推荐
相关产品推荐

