SwiftUI: Is it possible to create macOS HUDs like the ones that comes up when changing brightness/volume or building xode projects?

Viewed 108

I started playing with SwiftUI recently and i can't figure out how to create the kind of HUDs that come up when you build an Xcode project or when you change the screen brightness. Does anybody know how to recreate these? Here is an image for reference:

enter image description here

1 Answers

I was trying to implement this yesterday and finally kind of found a solution, maybe not perfect, but it works.

The basic idea is that you need to create a new borderless window and then close it after it is been displayed. This is the code I use, hope it helps you.

Button("Show HUD") {
    // create a borderless panel
    let window = NSPanel(contentRect: NSRect(x: 0, y: 0, width: 500, height: 500), styleMask: [.fullSizeContentView, .borderless, .hudWindow], backing: .buffered, defer: false)
                    
    window.center()

    // make transparent background

    window.backgroundColor = .clear
    
    let rect = NSScreen.main!.frame

    // move the window to bottom center
    let frame = NSRect(origin: window.frame.offsetBy(dx: 125, dy: -rect.height / 4).origin, size: window.frame.size)
                    
    window.setFrame(frame, display: true)

    // create hosting view
    window.contentView = NSHostingView(rootView: HUDView())
    
    // show the window
    window.makeKeyAndOrderFront(nil)

    // Auto close the window after 1.8s
    DispatchQueue.main.asyncAfter(deadline: .now() + 1.8) {
        window.close()
    }
}

And then define your HUDView

struct HUDView: View {
    @State var showHUD: Bool = false
    
    var body: some View {
        ZStack {
            VStack {}
            .background(.clear)
            .frame(width: 200, height: 200)
            .onAppear {
                withAnimation(.easeInOut(duration: 0.3)) {
                    showHUD = true
                }
            }
            if showHUD {
                VStack(spacing: 20) {
                    Image(systemName: "doc.on.clipboard.fill").font(.system(size: 72))
                    Text("Code Copied").font(.system(size: 20))
                }
                .frame(width: 200, height: 200)
                // the background could be .regularMaterial, but for some reason it can not works, so instead I created my own BlurView...
                .background(BlurView())
                .cornerRadius(20)
                .foregroundColor(.white.opacity(0.8))
                .overlay(RoundedRectangle(cornerRadius: 20).strokeBorder(.white.opacity(0.15), lineWidth: 1))
            }
        }
    }
}

BlurView defines

struct BlurView: NSViewRepresentable {
    func makeNSView(context: Context) -> NSVisualEffectView {
        let blurView = NSVisualEffectView()
        blurView.blendingMode = .behindWindow
        blurView.isEmphasized = true
        blurView.material = .hudWindow
        blurView.autoresizingMask = [.width, .height]
        blurView.state = NSVisualEffectView.State.active
        
        return blurView
    }
    
    func updateNSView(_ nsView: NSVisualEffectView, context: Context) {
    }
}

The preview should show something like below, pretty cool , isn't it?

enter image description here

Related