115

I'm looking to blur a view's background but don't want to have to break out into UIKit to accomplish it (eg. a UIVisualEffectView) I'm digging through docs and got nowhere, seemingly there is no way to live-clip a background and apply effects to it. Am I wrong or looking into it the wrong way?

0

9 Answers 9

282

1. The Native SwiftUI way:

Just add .blur() modifier on anything you need to be blurry like:

Image("BG")
   .blur(radius: 20)

Blur Demo 💡 Note the top and bottom of the view? you can set the opaque parameter to true to get rid of them.

Note that you can Group multiple views and blur them together.


2. The Visual Effect View:

You can bring the prefect UIVisualEffectView from the UIKit:

VisualEffectView(effect: UIBlurEffect(style: .dark))

With this tiny struct:

struct VisualEffectView: UIViewRepresentable {
    var effect: UIVisualEffect?
    func makeUIView(context: UIViewRepresentableContext<Self>) -> UIVisualEffectView { UIVisualEffectView() }
    func updateUIView(_ uiView: UIVisualEffectView, context: UIViewRepresentableContext<Self>) { uiView.effect = effect }
}

VEV Demo


3. iOS 15: Materials

You can use iOS predefined materials with one line code:

.background(.ultraThinMaterial)

Demo

Sign up to request clarification or add additional context in comments.

2 Comments

This is the most succinct use of Representable that I have seen. Very helpful.
there is no way to change the blur color of the Material background in SwiftUI - iOS 15
29

I haven't found a way to achieve that in SwiftUI yet, but you can use UIKit stuff via UIViewRepresentable protocol.

struct BlurView: UIViewRepresentable {

    let style: UIBlurEffect.Style

    func makeUIView(context: UIViewRepresentableContext<BlurView>) -> UIView {
        let view = UIView(frame: .zero)
        view.backgroundColor = .clear
        let blurEffect = UIBlurEffect(style: style)
        let blurView = UIVisualEffectView(effect: blurEffect)
        blurView.translatesAutoresizingMaskIntoConstraints = false
        view.insertSubview(blurView, at: 0)
        NSLayoutConstraint.activate([
            blurView.heightAnchor.constraint(equalTo: view.heightAnchor),
            blurView.widthAnchor.constraint(equalTo: view.widthAnchor),
        ])
        return view
    }

    func updateUIView(_ uiView: UIView,
                      context: UIViewRepresentableContext<BlurView>) {

    }

}

Demo:

struct ContentView: View {

    var body: some View {
        NavigationView {
            ZStack {
                List(1...100) { item in
                    Rectangle().foregroundColor(Color.pink)
                }
                .navigationBarTitle(Text("A List"))
                ZStack {
                    BlurView(style: .light)
                        .frame(width: 300, height: 300)
                    Text("Hey there, I'm on top of the blur")

                }
            }
        }
    }

}

I used ZStack to put views on top of it.

ZStack {
 // List
 ZStack {
    // Blurred View
    // Text
 }
}

And ends up looking like this:

enter image description here

6 Comments

This blurs content in a view but I’m looking for for something that allows blurring of underlying content, much like how a UI VisualEffectView or navigationController works whereas you can have an item that blurs the content it overlays and only within the frame of the BlurringView rather than child content of a BlurringView. I suppose more like how BlendMode works.
Isn't that what this is doing?
How do I reduce the amount of blur?
When I navigate the page second time the blur views do not appear. Is it because the updateUIView function is empty?
This is genius! It works - and if you use style 'systemChromeMaterial', it mimics how the menu bars have blur / transparency. Nice job.
|
28

The simplest way is here by Richard Mullinix:

struct Blur: UIViewRepresentable {
    var style: UIBlurEffect.Style = .systemMaterial

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

    func updateUIView(_ uiView: UIVisualEffectView, context: Context) {
        uiView.effect = UIBlurEffect(style: style)
    }
}

Then just use it somewhere in your code like background:

    //...
    MyView()
        .background(Blur(style: .systemUltraThinMaterial))

Comments

22

I have found an interesting hack to solve this problem. We can use UIVisualEffectView to make a live "snapshot" of its background. But this "snapshot" will have an applied effect of UIVisualEffectView. We can avoid applying this effect using UIViewPropertyAnimator.

I didn't find any side effects of this hack. You can find my solution here: my GitHub Gist

Code

/// A View in which content reflects all behind it
struct BackdropView: UIViewRepresentable {

    func makeUIView(context: Context) -> UIVisualEffectView {
        let view = UIVisualEffectView()
        let blur = UIBlurEffect()
        let animator = UIViewPropertyAnimator()
        animator.addAnimations { view.effect = blur }
        animator.fractionComplete = 0
        animator.stopAnimation(false)
        animator.finishAnimation(at: .current)
        return view
    }
    
    func updateUIView(_ uiView: UIVisualEffectView, context: Context) { }
    
}

/// A transparent View that blurs its background
struct BackdropBlurView: View {
    
    let radius: CGFloat
    
    @ViewBuilder
    var body: some View {
        BackdropView().blur(radius: radius)
    }
    
}

Usage

ZStack(alignment: .leading) {
    Image(systemName: "globe")
        .resizable()
        .frame(width: 200, height: 200)
        .foregroundColor(.accentColor)
        .padding()
    BackdropBlurView(radius: 6)
        .frame(width: 120)
}

Usage example

6 Comments

This is nice because it uses the old .blur(radius:), which gives more control over the amount of blurring than the new material styles. Even the ultraThinMaterial blurs quite a bit compared to, say, .blur(radius:3). The normal use of .blur is to apply it to the view you want to blur, but the approach here gives you a view that you can insert in a ZStack and blur the contents underneath. The other approaches that do this use the new (heavily blurring) materials.
Unfortunately it doesn't reach the edges of the screen for a full screen effect.
Shoot, just add the opaque: true parameter to the blur function and viola!!
Excellent solution! Is it safe to remove the animation and simply call view.effect = blur?
Amazing solution! (for more fine tuned blur)
|
12

New in iOS 15 , SwiftUI has a brilliantly simple equivalent to UIVisualEffectView, that combines ZStack, the background() modifier, and a range of built-in materials.

ZStack {
    Image("niceLook")

    Text("Click me")
        .padding()
        .background(.thinMaterial)
}

You can adjust the “thickness” of your material – how much of the background content shines through – by using one of several material types. From thinnest to thickest, they are:

.ultraThinMaterial
.thinMaterial
.regularMaterial
.thickMaterial
.ultraThickMaterial

Comments

7

There is a very useful but unfortunately private (thanks Apple) class CABackdropLayer

It draws a copy of the layers below, I found it useful when using blend mode or filters, It can also be used for blur effect

Code

open class UIBackdropView: UIView {

  open override class var layerClass: AnyClass {
    NSClassFromString("CABackdropLayer") ?? CALayer.self
  }
}

public struct Backdrop: UIViewRepresentable {

  public init() {}

  public func makeUIView(context: Context) -> UIBackdropView {
    UIBackdropView()
  }

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

public struct Blur: View {

  public var radius: CGFloat
  public var opaque: Bool

  public init(radius: CGFloat = 3.0, opaque: Bool = false) {
    self.radius = radius
    self.opaque = opaque
  }

  public var body: some View {
    Backdrop()
      .blur(radius: radius, opaque: opaque)
  }
}

Usage

struct Example: View {

  var body: some View {
    ZStack {
      YourBelowView()
      YourTopView()
        .background(Blur())
        .background(Color.someColor.opacity(0.4))
    }
  }
}

Source

Comments

6

As mentioned by @mojtaba, it's very peculiar to see white shade at top of image when you set resizable() along with blur().

As simple trick is to raise the Image padding to -ve.

 var body: some View {

        return
            ZStack {

                Image("background_2").resizable()
                    .edgesIgnoringSafeArea(.all)
                    .blur(radius: 5)
                    .scaledToFill()
                    .padding(-20) //Trick: To escape from white patch @top & @bottom


        }
  }

Result: SwiftUI Image blue trick

2 Comments

You can add another parameter to the .blur modifier. If you use .blur(radius: 5, opaque: true) it should remove that white shade.
Adding a black color that ignores safe area fixes the white halo effect.
6

Sometimes we need a transparent blur effect. Here will be a solution.

struct TransparentBlurView: UIViewRepresentable {
    typealias UIViewType = UIVisualEffectView

    func makeUIView(context: Context) -> UIVisualEffectView {
        let view = UIVisualEffectView(effect: UIBlurEffect(style: .systemUltraThinMaterialLight))

        return view
    }

    func updateUIView(_ uiView: UIVisualEffectView, context: Context) {
        DispatchQueue.main.async {
            if let backdropLayer  = uiView.layer.sublayers?.first {
                backdropLayer.filters?.removeAll(where: { filter in
                    String(describing: filter) != "gaussianBlur"
                })
            }
        }
    }
}

enter image description here

Comments

1
@State private var amount: CGFLOAT = 0.0

var body: some View {
    VStack{
       Image("Car").resizable().blur(radius: amount, opaque: true)
    }
}

Using "Opaque: true" with blur function will eliminate white noise

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.