画线并用 UIVisualEffectView 填充

Draw Line and fill with UIVisualEffectView

画线部分设置完成:

func drawLineFrom(_ fromPoint: CGPoint, toPoint: CGPoint) {

        let blurEffect = UIBlurEffect(style: UIBlurEffectStyle.dark)
        let blurEffectView = UIVisualEffectView(effect: blurEffect)

    UIGraphicsBeginImageContextWithOptions(tempImageView.frame.size, false, UIScreen.main.scale)

        let context = UIGraphicsGetCurrentContext()
        tempImageView.image?.draw(in: CGRect(x: 0, y: 0, width: tempImageView.frame.size.width, height: tempImageView.frame.size.height), blendMode: .normal, alpha: 1.0)

        context?.move(to: CGPoint(x: fromPoint.x, y: fromPoint.y))
        context?.addLine(to: CGPoint(x: toPoint.x, y: toPoint.y))

        context?.setLineCap(.round)
        context?.setLineWidth(brushWidth)
        context?.setStrokeColor(blurEffectView(effect: blurEffect))
        context?.setBlendMode(.normal)

        context!.strokePath()

        var img = UIGraphicsGetImageFromCurrentImageContext()
        tempImageView.image = img
        tempImageView.alpha = opacity

        UIGraphicsEndImageContext()

    }

它基本上使用了 touchesBegan、touchesMoved 和 touchesEnded,这就是 fromPointtoPoint 的来源。

就像您已经看到的那样,我正在尝试用 UIVisualEffectView 而不是 Color 来填充这一行。显然我的方法并没有真正奏效。 执行此操作的最佳解决方案是什么?

设置

A UIVisualEffectView 本身并没有任何内容,而是取决于它下面的视图。如果你想用 UIVisualEffect 给出绘图的效果,你应该构建你想要在其上绘图的视图层次结构。也许像带有图像视图的视图,显示一些图像,然后在其上显示效果视图,例如:

let imageView = UIImageView(image: UIImage(named: "image.jpg"))
let blurEffect = UIBlurEffect(style: .dark)
let effectView = UIVisualEffectView(effect: blurEffect)
effectView.frame = imageView.bounds
let view = UIView(frame: imageView.bounds)
view.addSubview(imageView)
view.addSubview(effectView)

然后您需要在此状态下对视图层次结构进行快照。如果它被触摸完全填充,这将像视图一样。您可以通过在 UIView:

上添加扩展名来做到这一点
extension UIView
{
    var snapshot: UIImage?
    {
        UIGraphicsBeginImageContextWithOptions(self.bounds.size, false, 0)
        self.drawHierarchy(in: self.bounds, afterScreenUpdates: true)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return image
    }
}

let image = view.snapshot!

我们现在可以将 UIVisualEffectView 替换为 UIImageView 保存我们的快照视图层次结构:

effectView.removeFromSuperview()
let topImageView = UIImageView(image: image)
view.addSubview(topImageView)

然后您可以通过声明 CAShapeLayer 属性 并将其设置为 topImageView

的遮罩层来遮罩 topImageView 绘制路径
let shapeLayer = CAShapeLayer()

topImageView.layer.mask = shapeLayer

绘图

现在您可以编写 drawLine 函数:

func drawLine(from fromPoint: CGPoint, to toPoint: CGPoint)
{
    let path: UIBezierPath
    if let layerPath = self.shapeLayer.path
    {
        path = UIBezierPath(cgPath: layerPath)
    }
    else
    {
        path = UIBezierPath()
    }
    path.move(to: fromPoint)
    path.addLine(to: toPoint)
    self.shapeLayer.path = path.cgPath
    self.shapeLayer.lineWidth = brushWidth
    self.shapeLayer.lineCap = "round"
    self.shapeLayer.strokeColor = UIColor.black.cgColor
}

在没有调用 drawLine 的实时视图中的游乐场中,我的视图如下所示:

调用后:

drawLine(from: CGPoint(x: 0, y: 0), to: CGPoint(x: imageView.frame.maxX, y: imageView.frame.maxY))
drawLine(from: CGPoint(x: 0, y: imageView.frame.maxY), to: CGPoint(x: imageView.frame.maxX, y: 0))

看起来像:

您应该知道这是处理器密集型的并且可能不高效。

更新

如果以后要更新视图层次结构或视觉效果,可以将 UIVisualEffectView 保留在视图层次结构中,而不是调用 removeFromSuperview 并隐藏它。然后重复快照和遮罩的过程。例如:

func updateMask(with visualEffect: UIVisualEffect)
{
    topImageView.isHidden = true
    effectView.isHidden = false

    effectView.effect = visualEffect
    topImageView.image = view.snapshot

    topImageView.isHidden = false
    effectView.isHidden = true
}

你可以看到我在游乐场使用的代码here.