SwiftUI中有模糊背景的方法吗?

yk9xbfzb  于 2023-06-21  发布在  Swift
关注(0)|答案(9)|浏览(112)

我希望模糊视图的背景,但不想打破到UIKit来完成它(例如。a UIVisualEffectView)我在文档中挖掘,没有任何进展,似乎没有办法实时剪辑背景并对其应用效果。是我错了还是调查的方法错了?

dy2hfwbg

dy2hfwbg1#

1.原生SwiftUI方式:

只需添加.blur()修改器上的任何你需要模糊像:

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


注意视图的顶部和底部

请注意您可以Group多个视图并将它们模糊在一起。

2.特效视图:

您可以从UIKit中获取完美的UIVisualEffectView

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

使用这个小结构:

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:材料

您可以使用iOS预定义材质,只需一行代码:

.background(.ultraThinMaterial)

qncylg1j

qncylg1j2#

我还没有在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")

                }
            }
        }
    }

}

我使用ZStack将视图放在它上面。

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

最后看起来像这样:

aiazj4mn

aiazj4mn3#

最简单的方法是Richard Mullinix的here

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

然后在代码中的某个地方使用它,比如background:

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

tf7tbtn24#

iOS 15中的新功能SwiftUIUIVisualEffectView有一个非常简单的等价物,它结合了ZStackbackground()修饰符和一系列内置材质。

ZStack {
    Image("niceLook")

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

您可以使用几种材质类型之一来调整材质的“厚度”-背景内容的亮度。从最薄到最厚,它们是:

.ultraThinMaterial
.thinMaterial
.regularMaterial
.thickMaterial
.ultraThickMaterial
dfuffjeb

dfuffjeb5#

正如@mojtaba所提到的,当你设置resizable()沿着blur()时,在图像顶部看到白色阴影是非常奇怪的。
一个简单的技巧是将Image padding提升到-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

        }
  }

结果:x1c 0d1x

zsbz8rwp

zsbz8rwp6#

我发现了一个有趣的黑客来解决这个问题。我们可以使用UIVisualEffectView来制作其背景的实时“快照”。但是这个“快照”将具有UIVisualEffectView的应用效果。我们可以使用UIViewPropertyAnimator来避免应用这种效果。
我没发现这个黑客有任何副作用。你可以在这里找到我的解决方案:my GitHub Gist

代码

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

用法

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

oo7oh9g9

oo7oh9g97#

@State private var amount: CGFLOAT = 0.0

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

使用“不透明:true”与模糊功能将消除白色噪声

2uluyalo

2uluyalo8#

有一个非常有用但不幸的私有(感谢Apple)类CABackdropLayer
它绘制了下面图层的副本,我发现它在使用blend mode或滤镜时很有用,它也可以用于模糊效果

代码

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

用法

struct Example: View {

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

Source

mfuanj7w

mfuanj7w9#

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

相关问题