我想要模糊一个视图的背景,但不想必须使用UIKit来实现它(例如:使用UIVisualEffectView
)。我查阅了文档,但没有找到合适的方法,看起来似乎不能够将背景限制在范围内并对其应用效果。我是否错了或者方法不对?
只需在需要模糊效果的任何内容上添加.blur()
修饰符,例如:
Image("BG")
.blur(radius: 20)
请注意,您可以将多个视图分组并将它们一起模糊。
您可以从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 }
}
你可以使用一行代码来使用iOS预定义的材料:
.background(.ultraThinMaterial)
我还没有找到在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>) {
}
}
示例:
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
}
}
最终呈现效果如下:
最简单的方法是来自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)
}
}
然后只需要在代码中像背景一样使用它:
//...
MyView()
.background(Blur(style: .systemUltraThinMaterial))
iOS 15 中的新功能,SwiftUI
提供了一个非常简单的替代方法来实现 UIVisualEffectView
,它结合了 ZStack
、background()
修饰符以及一系列内置的材料。
ZStack {
Image("niceLook")
Text("Click me")
.padding()
.background(.thinMaterial)
}
.ultraThinMaterial
.thinMaterial
.regularMaterial
.thickMaterial
.ultraThickMaterial
UIVisualEffectView
来实时“快照”其背景。但是这个“快照”会应用 UIVisualEffectView
的效果。我们可以使用 UIViewPropertyAnimator
来避免应用这个效果。/// 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)
}
.blur(radius:)
,比新的材料样式更能控制模糊的程度。即使是ultraThinMaterial
相对于.blur(radius:3)
也会有相当大的模糊效果。.blur
的正常用法是将其应用于要模糊的视图,但这里的方法提供了一个视图,您可以将其插入到ZStack中并模糊下面的内容。其他实现此功能的方法使用新的(强烈模糊)材料。 - James Toomeyopaque: true
参数到模糊函数里,就搞定了! - undefined.blur
修饰符中添加另一个参数。如果你使用 .blur(radius: 5, opaque: true)
,它应该会去除那个白色阴影。 - Gareth Lewis@State private var amount: CGFLOAT = 0.0
var body: some View {
VStack{
Image("Car").resizable().blur(radius: amount, opaque: true)
}
}
有一个非常有用但不幸的是私有的(感谢苹果)类CABackdropLayer
它可以绘制下面的图层的副本,当使用混合模式
或滤镜时我发现它很有用,也可以用于模糊效果
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))
}
}
}
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"
})
}
}
}
}
Button("Test") {}
.background(Rectangle().fill(Color.red).blur(radius: 20))
Material
背景的模糊颜色。 - JAHelia