在 SwiftUI 中重新创建蒙版模糊效果

Posted

技术标签:

【中文标题】在 SwiftUI 中重新创建蒙版模糊效果【英文标题】:Recreating a masked blur effect in SwiftUI 【发布时间】:2020-12-16 16:58:36 【问题描述】:

我已经创建了这个蒙版模糊效果(下面的代码),它在 SwiftUI 中运行,但使用 UIViewRepresentable 进行蒙版,是否可以重新创建相同的效果,但只是在纯 SwiftUI 中?

这是当前代码,如果你运行它,用手指在屏幕上拖动,这会移动遮罩以显示其下方。

import SwiftUI
import UIKit

struct TestView: View 

  @State var position: CGPoint = .zero

  var simpleDrag: some Gesture 
    DragGesture()
      .onChanged  value in
        self.position = value.location
      
  

  var body: some View 
    ZStack 
  
      Circle()
        .fill(Color.green)
        .frame(height: 200)
  
      Circle()
        .fill(Color.pink)
        .frame(height: 200)
        .offset(x: 50, y: 100)
  
      Circle()
        .fill(Color.orange)
        .frame(height: 100)
        .offset(x: -50, y: 00)
  
      BlurView(style: .light, position: $position)
        .frame(maxWidth: .infinity, maxHeight: .infinity)
  
    
    .gesture(
      simpleDrag
    )

  


struct BlurView: UIViewRepresentable 

  let style: UIBlurEffect.Style

  @Binding var position: CGPoint

  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),
    ])

    let clipPath = UIBezierPath(rect: UIScreen.main.bounds)

    let circlePath = UIBezierPath(ovalIn: CGRect(x: 100, y: 0, width: 200, height: 200))

    clipPath.append(circlePath)

    let layer = CAShapeLayer()
    layer.path = clipPath.cgPath
    layer.fillRule = .evenOdd
    view.layer.mask = layer
    view.layer.masksToBounds = true

    return view
  

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

    let clipPath = UIBezierPath(rect: UIScreen.main.bounds)

    let circlePath = UIBezierPath(ovalIn: CGRect(x: position.x, y: position.y, width: 200, height: 200))

    clipPath.append(circlePath)

    let layer = CAShapeLayer()
    layer.path = clipPath.cgPath
    layer.fillRule = .evenOdd
    uiView.layer.mask = layer
    uiView.layer.masksToBounds = true

  



struct TestView_Previews: PreviewProvider 
  static var previews: some View 
    TestView()
  

【问题讨论】:

【参考方案1】:

我想我几乎有一个解决方案,我可以使用 viewmodifer 使用 ZStack 将结果渲染两次,我可以模糊一个视图,并使用蒙版在其中敲一个洞。

import SwiftUI

struct TestView2: View 

  @State var position: CGPoint = .zero

  var simpleDrag: some Gesture 
    DragGesture()
      .onChanged  value in
        self.position = value.location
      
  

  var body: some View 
    ZStack 
  
      Circle()
        .fill(Color.green)
        .frame(height: 200)
  
      Circle()
        .fill(Color.pink)
        .frame(height: 200)
        .offset(x: 50, y: 100)
  
      Circle()
        .fill(Color.orange)
        .frame(height: 100)
        .offset(x: -50, y: 00)
    
    .maskedBlur(position: $position)
    .gesture(
      simpleDrag
    )

  


struct MaskedBlur: ViewModifier 

  @Binding var position: CGPoint

  /// Render the content twice
  func body(content: Content) -> some View 

    ZStack 
      content
  
      content
        .blur(radius: 10)
        .mask(
          Hole(position: $position)
            .fill(style: FillStyle(eoFill: true))
            .frame(maxWidth: .infinity, maxHeight: .infinity)
        )
    

  


extension View 
  func maskedBlur(position: Binding<CGPoint>) -> some View 
    self.modifier(MaskedBlur(position: position))
  


struct Hole: Shape 

  @Binding var position: CGPoint

  func path(in rect: CGRect) -> Path 
    var path = Path()

    path.addRect(UIScreen.main.bounds)

    path.addEllipse(in: CGRect(x: position.x, y: position.y, width: 200, height: 200))

    return path
  



#if DEBUG

struct TestView2_Previews: PreviewProvider 
  static var previews: some View 
    TestView2()
  


#endif

【讨论】:

以上是关于在 SwiftUI 中重新创建蒙版模糊效果的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI:使用具有可变模糊效果的 RotationGesture

SpriteKit中的磨砂玻璃效果?

高斯模糊效果

UITableViewCell 中没有出现模糊效果

在滚动上使用 css 和 jQuery 创建模糊效果

创建具有微妙模糊效果的透明 UIView