【问题标题】:Recreating a masked blur effect in SwiftUI在 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()
  }
}

【问题讨论】:

    标签: swiftui uikit


    【解决方案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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-10-26
      • 2016-04-11
      • 1970-01-01
      • 1970-01-01
      • 2014-10-05
      • 2020-05-05
      • 1970-01-01
      相关资源
      最近更新 更多