UINavigationItem扩展中弹出UIAlertController的实现问题
你遇到的核心问题是 UINavigationItem 本身不是控制器类型,没有 present(_:animated:completion:) 方法,所以无法直接在它的扩展方法里弹出弹窗,同时你原有代码中的 UIAlertController 初始化缺少必填的 preferredStyle 参数,运行时会触发崩溃,下面提供两种可直接落地的实现方案:
方案1:扩展内获取顶层控制器弹出弹窗
不需要改动原有调用逻辑,只需要补充获取当前应用最顶层显示控制器的工具方法,拿到控制器后直接弹出alert即可,修改后的完整扩展代码如下:
extension UINavigationItem { func barButton() { let barButton = UIBarButtonItem() barButton.image = UIImage(systemName: "person.fill") barButton.tintColor = .white barButton.action = #selector(barButtonClicked) barButton.target = self self.rightBarButtonItem = barButton } // 工具方法:适配iOS13+ 获取当前顶层显示的控制器 private func getTopViewController() -> UIViewController? { let keyWindow = UIApplication.shared.connectedScenes .filter { $0.activationState == .foregroundActive } .compactMap { $0 as? UIWindowScene } .first?.windows .first { $0.isKeyWindow } var topController = keyWindow?.rootViewController while let presentedController = topController?.presentedViewController { topController = presentedController } return topController } @objc func barButtonClicked() { // 补上必填的preferredStyle参数,可选.alert或.actionSheet let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) // 补充取消按钮避免弹窗无法关闭 alertController.addAction(UIAlertAction(title: "取消", style: .cancel)) let logoutAction = UIAlertAction(title: "Log Out", style: .destructive) { _ in print("logout") } alertController.addAction(logoutAction) let historyAction = UIAlertAction(title: "History", style: .default) { _ in print("history") } alertController.addAction(historyAction) // 调用顶层控制器弹出弹窗 guard let topVC = getTopViewController() else { return } topVC.present(alertController, animated: true) } }
调用方式和你原有逻辑完全一致,在各控制器的 viewDidLoad 里调用 self.navigationItem.barButton() 即可正常使用。
方案2:回调透传点击事件给所属控制器
如果不同控制器需要实现不同的点击逻辑,推荐用回调把处理权交给调用方,灵活性更高,实现代码如下:
import ObjectiveC extension UINavigationItem { // 用关联对象存储回调闭包 private var barButtonAction: (() -> Void)? { get { objc_getAssociatedObject(self, &AssociatedKeys.actionKey) as? () -> Void } set { objc_setAssociatedObject(self, &AssociatedKeys.actionKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } private struct AssociatedKeys { static var actionKey = "barButtonActionKey" } func setupBarButton(action: @escaping () -> Void) { self.barButtonAction = action let barButton = UIBarButtonItem() barButton.image = UIImage(systemName: "person.fill") barButton.tintColor = .white barButton.action = #selector(barButtonClicked) barButton.target = self self.rightBarButtonItem = barButton } @objc private func barButtonClicked() { barButtonAction?() } }
调用时直接在对应控制器里处理弹窗逻辑即可:
// 视图控制器的viewDidLoad中调用 self.navigationItem.setupBarButton { [weak self] in guard let self = self else { return } let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) alertController.addAction(UIAlertAction(title: "取消", style: .cancel)) alertController.addAction(UIAlertAction(title: "Log Out", style: .destructive) { _ in print("logout") }) alertController.addAction(UIAlertAction(title: "History", style: .default) { _ in print("history") }) self.present(alertController, animated: true) }
内容的提问来源于stack exchange,提问作者TayaR
相关产品推荐
相关产品推荐

