3

Is there any way to add Activity View (Indicator) into SwiftUI Alert somewhere? I'm just curious because I haven't found any appropriate answer on such question. I need something like this: enter image description here

I'm using iOS 14 SwiftUI Alert with optional state that conforms to Identifiable. There was a way in UIKit UIAlertController to add subview to the alert's view. Is there some ideas on that, thanks in advance.

3 Answers3

3

I had to something similar in an app and basically it is not possible using the native SwiftUI .alert API. You can

  • Use a custom UIAlertController
  • Make a custom overlay that does what you want

Because of that I created CustomAlert so I can easily make alerts with custom content. It essentially recreates the alert in SwiftUI and exposes a similar API for it.

.customAlert(isPresented: $alertShown) {
    HStack(spacing: 16) {
        ProgressView()
            .progressViewStyle(.circular)
            .tint(.blue)
        Text("Processing...")
            .font(.headline)
    }
} actions: {
    Button(role: .cancel) {
        // Cancel Action
    } label: {
        Text("Cancel")
    }
}
hallo
  • 825
  • 1
  • 7
  • 10
  • 3
    Thank you, you are right! I have investigated this question. And unfortunately there is no any way to do that with native SwiftUI Alert. I took a look at [CustomAlert](https://github.com/divadretlaw/CustomAlert) that you have shared and it's really good one. But we decided to create simple `ProgressAlertView` for this target in our project to avoid additional SMP dependencies. I will share that piece of code a bit later. – Oleksandr Yevdokymov Oct 02 '22 at 18:38
  • 1
    I've shared own solution – Oleksandr Yevdokymov Feb 27 '23 at 10:29
2

To avoid SPM dependency I've created ProgressAlert in project, so as I mentioned in the comment of hallo's post I would like to share this code with you:

import SwiftUI

public struct ProgressAlert: View {
    public var closeAction: () -> Void

    public init(closeAction: @escaping () -> Void) {
        self.closeAction = closeAction
    }

    public var body: some View {
        ZStack {
            VStack(spacing: 14) {
                HStack(spacing: 20) {
                    ProgressView()
                        .scaleEffect(1.5)
                        .progressViewStyle(CircularProgressViewStyle(tint: Color(UIColor(red: 0.05, green: 0.64, blue: 0.82, alpha: 1))))
                    Text("Processing...")
                        .font(.system(size: 16, weight: .semibold))
                        .foregroundColor(.black)
                }
                Divider()
                Button(action: closeAction, label: {
                    Text("Cancel")
                        .font(.headline)
                        .foregroundColor(Color(UIColor(red: 0.05, green: 0.64, blue: 0.82, alpha: 1)))
                })
                .foregroundColor(.black)
            }
            .padding(.vertical, 25)
            .frame(maxWidth: 270)
            .background(BlurView(style: .systemMaterial))
            .cornerRadius(20)
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .background(
            Color.primary.opacity(0.35)
        )
        .edgesIgnoringSafeArea(.all)
    }
}

public struct BlurView: UIViewRepresentable {
    public var style: UIBlurEffect.Style

    public func makeUIView(context: Context) -> UIVisualEffectView {
        let view = UIVisualEffectView(effect: UIBlurEffect(style: style))
        return view
    }

    public func updateUIView(_ uiView: UIVisualEffectView, context: Context) {}
}

public struct ProgressAlert_Previews: PreviewProvider {
    static public var previews: some View {
        ProgressAlert(closeAction: {
            print("")
        })
    }
}

Here is how it looks:

ProgressAlert

1

For maximum control over the content and behaviour of the alert popup, I recommend just creating your own

struct ContentView: View {
    var alertShown: Bool = false
    var body: some View {
        ZStack {
            VStack {
                // your main view
            }
            .blur(radius: alertShown ? 15 : 0)
            if alertShown {
                AlertView()
            }
        }
    }
}

struct AlertView: View {
    var body: some View {
        ZStack {
            RoundedRectangle(cornerRadius: 6)
                .foregroundColor(.blue)
            VStack {
                HStack {
                    ProgressView()
                    Text("Processing...")
                }
                Button(action: {
                    // action
                }, label: {
                    Text("Cancel")
                })
                .foregroundColor(.black)
            }
        }
    }
}

Suyash Medhavi
  • 1,135
  • 6
  • 18