Blur background with SwiftUI
For the moment SwiftUI doesnt provide a native way to create blur effect, and depending on your target it could be difficult to do. After some research in the topic, the simplest way that I found was inside of the code shared by apple itself. here you have the implementation for both iOS and macOS.
iOS Implementation
import SwiftUI
// MARK: - VisualEffectBlur
struct VisualEffectBlur<Content: View>: View {
var blurStyle: UIBlurEffect.Style
var vibrancyStyle: UIVibrancyEffectStyle?
var content: Content
init(blurStyle: UIBlurEffect.Style = .systemMaterial, vibrancyStyle: UIVibrancyEffectStyle? = nil, @ViewBuilder content: () -> Content) {
self.blurStyle = blurStyle
self.vibrancyStyle = vibrancyStyle
self.content = content()
}
var body: some View {
Representable(blurStyle: blurStyle, vibrancyStyle: vibrancyStyle, content: ZStack { content })
.accessibility(hidden: Content.self == EmptyView.self)
}
}
// MARK: - Representable
extension VisualEffectBlur {
struct Representable<Content: View>: UIViewRepresentable {
var blurStyle: UIBlurEffect.Style
var vibrancyStyle: UIVibrancyEffectStyle?
var content: Content
func makeUIView(context: Context) -> UIVisualEffectView {
context.coordinator.blurView
}
func updateUIView(_ view: UIVisualEffectView, context: Context) {
context.coordinator.update(content: content, blurStyle: blurStyle, vibrancyStyle: vibrancyStyle)
}
func makeCoordinator() -> Coordinator {
Coordinator(content: content)
}
}
}
// MARK: - Coordinator
extension VisualEffectBlur.Representable {
class Coordinator {
let blurView = UIVisualEffectView()
let vibrancyView = UIVisualEffectView()
let hostingController: UIHostingController<Content>
init(content: Content) {
hostingController = UIHostingController(rootView: content)
hostingController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
hostingController.view.backgroundColor = nil
blurView.contentView.addSubview(vibrancyView)
blurView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
vibrancyView.contentView.addSubview(hostingController.view)
vibrancyView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
}
func update(content: Content, blurStyle: UIBlurEffect.Style, vibrancyStyle: UIVibrancyEffectStyle?) {
hostingController.rootView = content
let blurEffect = UIBlurEffect(style: blurStyle)
blurView.effect = blurEffect
if let vibrancyStyle = vibrancyStyle {
vibrancyView.effect = UIVibrancyEffect(blurEffect: blurEffect, style: vibrancyStyle)
} else {
vibrancyView.effect = nil
}
hostingController.view.setNeedsDisplay()
}
}
}
// MARK: - Content-less Initializer
extension VisualEffectBlur where Content == EmptyView {
init(blurStyle: UIBlurEffect.Style = .systemMaterial) {
self.init( blurStyle: blurStyle, vibrancyStyle: nil) {
EmptyView()
}
}
}
// MARK: - Previews
struct VisualEffectBlur_Previews: PreviewProvider {
static var previews: some View {
ZStack {
LinearGradient(
gradient: Gradient(colors: [.red, .blue]),
startPoint: .topLeading,
endPoint: .bottomTrailing
)
VisualEffectBlur(blurStyle: .systemUltraThinMaterial, vibrancyStyle: .fill) {
Text("Hello World!")
.frame(width: 200, height: 100)
}
}
.previewLayout(.sizeThatFits)
}
}
macOS Implementation
/*
See LICENSE folder for this sample’s licensing information.
Abstract:
The macOS implementation of a NSVisualEffectView's blur.
*/
import SwiftUI
// MARK: - VisualEffectBlur
struct VisualEffectBlur: View {
var material: NSVisualEffectView.Material
init(material: NSVisualEffectView.Material = .headerView) {
self.material = material
}
var body: some View {
Representable(material: material)
.accessibility(hidden: true)
}
}
// MARK: - Representable
extension VisualEffectBlur {
struct Representable: NSViewRepresentable {
var material: NSVisualEffectView.Material
func makeNSView(context: Context) -> NSVisualEffectView {
context.coordinator.visualEffectView
}
func updateNSView(_ view: NSVisualEffectView, context: Context) {
context.coordinator.update(material: material)
}
func makeCoordinator() -> Coordinator {
Coordinator()
}
}
class Coordinator {
let visualEffectView = NSVisualEffectView()
init() {
visualEffectView.blendingMode = .withinWindow
}
func update(material: NSVisualEffectView.Material) {
visualEffectView.material = material
}
}
}
// MARK: - Previews
struct VisualEffectView_Previews: PreviewProvider {
static var previews: some View {
ZStack {
LinearGradient(gradient: Gradient(colors: [.red, .blue]), startPoint: .topLeading, endPoint: .bottomTrailing)
VisualEffectBlur()
.padding()
Text("Hello World!")
}
.frame(width: 200, height: 100)
.previewLayout(.sizeThatFits)
}
}
Don't worry, be pathient!