06 January 2023

Adding Blur Effects to a macOS App using SwiftUI

4 minute read

I recently started learning SwiftUI to build a new project called Xnappit targeted at macOS and it has been quite a ride learning Swift and SwiftUI.

SwiftUI is a powerful framework for building native apps on Apple platforms. One of the great things about SwiftUI is its ability to work seamlessly with AppKit and UIKit, the frameworks used for building Mac and iOS apps respectively. In this blog post, we’ll take a look at how to use a NSVisualEffectView in a SwiftUI app using NSViewRepresentable.

A NSVisualEffectView is a view that provides a blur effect to the content behind it. It’s a useful tool for adding depth and polish to your app’s interface.

To use a NSVisualEffectView in a SwiftUI app, we’ll need to create a custom View type that conforms to the NSViewRepresentable protocol. In the example below we create a struct called BlurredView which conforms to the NSViewRepresentable protocol. This protocol has two methods that we’ll need to implement: makeNSView(context:) and updateNSView(_:context:).

import SwiftUI

struct BlurView: NSViewRepresentable {
  ...
}

The makeNSView(context:) method is called when the view is first created. Here, we can create a new NSVisualEffectView instance and configure it as needed. In the example below, we set the material property to .sidebar and the blendingMode property to .behindWindow.

func makeNSView(context: Context) -> some NSVisualEffectView {
    let view = NSVisualEffectView()
    view.material = .sidebar
    view.blendingMode = .behindWindow
    return view
}

The updateNSView(_:context:) method is called when the view’s state changes. In this method, we can update the NSVisualEffectView instance with the new state. In the example below, the method is left empty since the view’s state isn’t changing.

func updateNSView(_ nsView: NSViewType, context: Context) {
}

With these two methods implemented we have the following:

import SwiftUI

struct BlurredView: NSViewRepresentable {
    func makeNSView(context: Context) -> some NSVisualEffectView {
        let view = NSVisualEffectView()
        view.material = .sidebar
        view.blendingMode = .behindWindow
        
        return view
    }
    
    func updateNSView(_ nsView: NSViewType, context: Context) {
        
    }
}

We can now use our BlurredView struct as a regular SwiftUI view. Simply add it to your view hierarchy like any other view, and the blur effect will be applied to the content behind it. Here we add it as a background of a VStack.

struct ContentView: View {
    var body: some View {
        VStack { 
            Text("Hello, World!")
        }.background(BlurredView()).frame(width: 300, height: 300)
    }
}

Using NSViewRepresentable is a great way to leverage the power of AppKit and UIKit in your SwiftUI apps. It allows you to use familiar UI components and techniques, while still taking advantage of the declarative syntax and improved performance of SwiftUI.