如何在SwiftUI Alert中集成Activity Indicator(活动指示器)?
嘿,这个问题我之前也碰到过——SwiftUI 原生的 Alert 确实没有直接提供添加 Activity Indicator 的接口,不过我们有两种靠谱的实现思路,正好适配你 iOS 14 的场景,我给你详细说说:
方案一:自定义模态视图替代原生 Alert
因为 SwiftUI 原生 Alert 的扩展性很差,自定义一个模态视图是最灵活的方式,完全可以按照你的需求布局,还能保持和原生 Alert 类似的交互逻辑(用遵循 Identifiable 的可选状态来控制显示)。
代码示例如下:
// 定义遵循 Identifiable 的 Alert 状态模型 struct LoadingAlert: Identifiable { let id = UUID() let message: String let isLoading: Bool } struct ContentView: View { // 用可选类型控制 Alert 显示/隐藏 @State private var loadingAlert: LoadingAlert? var body: some View { Button("Show Loading Alert") { loadingAlert = LoadingAlert(message: "Processing...", isLoading: true) } // 通过 overlay 实现居中的模态弹窗 .overlay( Group { if let alert = loadingAlert { // 半透明背景 Color.black.opacity(0.5) .ignoresSafeArea() .overlay( // 弹窗内容卡片 VStack(spacing: 16) { Text(alert.message) .foregroundColor(.black) .font(.headline) // SwiftUI 自带的活动指示器(iOS 13+ 可用,适配 iOS 14) if alert.isLoading { ProgressView() .tint(.blue) } Button("Cancel") { loadingAlert = nil } .buttonStyle(.borderedProminent) } .padding() .background(Color.white) .cornerRadius(12) .shadow(radius: 10) ) } } ) } }
这个方案的优势:
- 完全自定义布局,你可以调整文字样式、指示器位置、按钮样式,甚至添加更多元素
- 逻辑和原生 Alert 一致,用
Identifiable的可选状态控制显示,学习成本低 - 适配 iOS 14,不需要额外的兼容性处理
方案二:桥接 UIKit 的 UIAlertController 实现
既然你提到 UIKit 里可以给 UIAlertController 添加子视图,那我们可以在 SwiftUI 中通过 ViewModifier 来封装这个逻辑,既能利用 UIKit 的灵活性,又能贴近原生 Alert 的样式。
代码示例:
// 自定义 ViewModifier,封装 UIKit Alert 的逻辑 struct LoadingAlertModifier: ViewModifier { let message: String // 用绑定控制 Alert 显示/隐藏,也可以改成 Identifiable 的可选类型 @Binding var isPresented: Bool func body(content: Content) -> some View { content .onChange(of: isPresented) { newValue in if newValue { presentLoadingAlert() } } } private func presentLoadingAlert() { // 获取当前根视图控制器(iOS 14 可用该方式) guard let rootVC = UIApplication.shared.windows.first?.rootViewController else { return } // 创建 UIAlertController let alert = UIAlertController(title: nil, message: message, preferredStyle: .alert) // 添加 Activity Indicator let activityIndicator = UIActivityIndicatorView(style: .medium) activityIndicator.translatesAutoresizingMaskIntoConstraints = false activityIndicator.startAnimating() alert.view.addSubview(activityIndicator) // 设置指示器的居中约束 NSLayoutConstraint.activate([ activityIndicator.centerXAnchor.constraint(equalTo: alert.view.centerXAnchor), activityIndicator.bottomAnchor.constraint(equalTo: alert.view.bottomAnchor, constant: -20) ]) // 添加取消按钮并绑定关闭逻辑 let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { _ in isPresented = false } alert.addAction(cancelAction) // 弹出 Alert rootVC.present(alert, animated: true) } } // 给 View 添加扩展,方便调用 extension View { func loadingAlert(message: String, isPresented: Binding<Bool>) -> some View { self.modifier(LoadingAlertModifier(message: message, isPresented: isPresented)) } } // 使用示例 struct ContentView: View { @State private var showAlert = false var body: some View { Button("Show Loading Alert") { showAlert = true } .loadingAlert(message: "Processing...", isPresented: $showAlert) } }
如果想要适配你提到的 Identifiable 可选状态,只需要把 Binding<Bool> 改成 Binding<LoadingAlert?>,然后在 onChange 里判断是否有值即可,逻辑和原生 Alert 完全对齐。
这个方案的优势:
- 完全复用 UIKit 的原生 Alert 样式,视觉上和系统保持一致
- 利用你已有的 UIKit 知识,不需要重新设计弹窗布局
总结
如果需要高度自定义弹窗样式,优先选方案一;如果想保持原生 Alert 的视觉风格,方案二更合适。两种方案都能在 iOS 14 上稳定运行,你可以根据自己的需求选择。
内容的提问来源于stack exchange,提问作者Oleksandr Yevdokymov
相关产品推荐
相关产品推荐

