简体   繁体   English

SwiftUI有模糊背景的方法吗?

[英]Is there a method to blur a background in SwiftUI?

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.我正在寻找模糊视图的背景,但不想进入 UIKit 来完成它(例如UIVisualEffectView )我正在挖掘文档但无处可去,似乎没有办法实时剪辑 a背景并对其应用效果。 Am I wrong or looking into it the wrong way?我错了还是以错误的方式看待它?

1. The Native SwiftUI way: 1. Native SwiftUI方式:

Just add .blur() modifier on anything you need to be blurry like:只需在需要模糊的任何内容上添加.blur()修饰符,例如:

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

模糊演示 Note the top and bottom of the view注意视图的顶部和底部

Note that you can Group multiple views and blur them together.请注意,您可以对多个视图进行Group并将它们模糊在一起。


2. The Visual Effect View: 2. 视觉效果视图:

You can bring the prefect UIVisualEffectView from the UIKit:你可以从 UIKit 中引入完美的UIVisualEffectView

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 }
}

电动汽车演示


3. iOS 15: Materials 3. iOS 15:材料

You can use iOS predefined materials with one line code:您可以使用一行代码使用 iOS 预定义材料:

.background(.ultraThinMaterial)

演示

I haven't found a way to achieve that in SwiftUI yet, but you can use UIKit stuff via UIViewRepresentable protocol.我还没有找到在 SwiftUI 中实现这一点的方法,但是您可以通过UIViewRepresentable协议使用 UIKit 的东西。

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将视图放在它上面。

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

And ends up looking like this:最终看起来像这样:

在此处输入图片说明

The simplest way is here by Richard Mullinix:最简单的办法就是在这里由理查德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))

As mentioned by @mojtaba, it's very peculiar to see white shade at top of image when you set resizable() along with blur().正如@mojtaba 所提到的,当您设置resizable()和 blur() 时,在图像顶部看到白色阴影是非常奇特的。

As simple trick is to raise the Image padding to -ve.一个简单的技巧是将图像填充提高到 -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 蓝色技巧

@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使用带有模糊功能的“Opaque: true”将消除白噪声

I have found an interesting hack to solve this problem.我发现了一个有趣的 hack 来解决这个问题。 We can use UIVisualEffectView to make live "snapshot" of its background.我们可以使用UIVisualEffectView制作其背景的实时“快照”。 But this "snapshot" will have an applied effect of UIVisualEffectView .但是这个“快照”将具有UIVisualEffectView的应用效果。 We can avoid applying this effect using UIViewPropertyAnimator .我们可以使用UIViewPropertyAnimator避免应用这种效果。

I didn't find any side effect of this hack.我没有发现这个 hack 的任何副作用。 You can find my solution here: my GitHub Gist你可以在这里找到我的解决方案: my GitHub Gist

Code代码

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

    func makeUIView(context: Context) -> UIVisualEffectView {
        let view = UIVisualEffectView()
        let blur = UIBlurEffect(style: .extraLight)
        let animator = UIViewPropertyAnimator()
        animator.addAnimations { view.effect = blur }
        animator.fractionComplete = 0
        animator.stopAnimation(true)
        animator.finishAnimation(at: .start)
        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)
}

使用示例

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.在 iOS 15 中, SwiftUI有一个非常简单的UIVisualEffectView等效UIVisualEffectView ,它结合了ZStackbackground()修饰符和一系列内置材料。

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

There is a very useful but unfortunately private (thanks Apple) class CABackdropLayer有一个非常有用但不幸的是私有(感谢 Apple)类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它绘制了下面图层的副本,我发现它在使用blend mode或滤镜时很有用,它还可以用于模糊效果

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 来源

Button("Test") {}
        .background(Rectangle().fill(Color.red).blur(radius: 20))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM