繁体   English   中英

如何访问在 draw(_ rect: CGRect) 中绘制的 UIBezierPath?

[英]how can i access UIBezierPath that is drawn in draw(_ rect: CGRect)?

我在 draw(_ rect: CGRect) 方法中使用 UIBezierPath 绘制线条之前我确实尝试过使用 CAShapeLayer 但我无法 select 特定路径并移动它所以我正在尝试这个,但在绘制之后我不是能够从视图的子视图或其子层而不是直接从 UIView 访问该 BezierPath。 我如何访问在 drawRect 方法中绘制的贝塞尔路径,以便我可以根据触摸更改它的 position。

private var bezierPaths: [MyBezierPath] = [MyBezierPath]()

    override func draw(_ rect: CGRect) {
    super.draw(rect)
    UIColor.orange.set()
    for path in bezierPaths {
        path.lineWidth = 4
        path.lineCapStyle = .round
        path.lineJoinStyle = .round
        path.stroke()
    }
}

    func drawingPath(_ path: [MyBezierPath]) {
    bezierPaths = path
}

就像创建 CAShapeLayer 和设置 layer.path = BezierPath 一样:

        let shapeLayer = CAShapeLayer()
        shapeLayer.lineWidth = 1
        shapeLayer.strokeColor = UIColor.black.cgColor
        shapeLayer.fillColor = UIColor.clear.cgColor
        shapeLayer.lineCap = .round
        shapeLayer.lineJoin = .round
        shapeLayer.lineDashPattern = [10, 10]
        shapeLayer.name = "ShapeLayer"
        self.canvas.layer.addSublayer(shapeLayer)
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {

if canvas.layer.sublayers != nil && canvas.layer.sublayers?.last?.name == "ShapeLayer" {
                guard let layer = canvas.layer.sublayers?.last as? CAShapeLayer else { return }
                layer.path = path.cgPath
            }
}

像这样我可以访问 layer.path 但我如何才能在 draw(Rect:) 方法中绘制路径呢?

根据您实际尝试执行的操作,覆盖draw(_:)可能不是最好的方法。

例如,如果您想为绘制的路径设置动画,那么使用CAShapeLayer作为子图层或使用子视图会容易得多。

此外,形状层是高度优化的……如果您编写自己的绘图/动画函数,您可能会遇到优化程度较低的代码。

但是,无论您采用哪种方法,“找到路径”您要使用contains(_ point: CGPoint)

例如,如果您有一个UIBezierPath数组,其中所有路径都相对于视图的左上角 ( 0,0 ),那么您可以在触摸时执行以下操作:

// find a path that contains the touch point
if let touchedPath = bezierPaths.first(where: {$0.contains(point)}) {
    // do something with that path
}

如果路径相对于视图的左上角 - 例如,如果路径相对于层 position,或者是子视图的一部分 - 您需要转换触摸点。

这是一个看起来像这样的快速示例 - 在加载时,然后在拖动一些形状后:

在此处输入图像描述 在此处输入图像描述

我们将从一个简单的“路径”结构开始,其中包含贝塞尔路径和要使用的颜色。 我们可以添加各种其他属性,例如线宽、虚线图案等:

struct MyPath {
    var color: UIColor = .white
    var path: UIBezierPath = UIBezierPath()
}

然后我们将使用这个UIView子类来处理绘图以及开始/移动/结束的触摸:

class BezDrawView: UIView {
    
    var myPaths: [MyPath] = [] {
        didSet {
            setNeedsDisplay()
        }
    }
    
    // used to track path to move
    var activePath: MyPath?
    var startPoint: CGPoint = .zero
    
    override func draw(_ rect: CGRect) {
        super.draw(rect)
        
        if myPaths.count > 0 {
            myPaths.forEach { p in
                p.color.set()
                p.path.stroke()
            }
        }
        
    }
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let t = touches.first else { return }
        let point = t.location(in: self)
        // find a path that contains the touch point
        if let touchedPath = myPaths.first(where: {$0.path.contains(point)}) {
            self.activePath = touchedPath
            self.startPoint = point
            return
        }
    }
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let ap = activePath, let t = touches.first else { return }
        let point = t.location(in: self)
        // move the path by the distance the touch moved
        let tr = CGAffineTransform(translationX: point.x - startPoint.x, y: point.y - startPoint.y)
        ap.path.apply(tr)
        startPoint = point
        // this triggers draw(_:)
        setNeedsDisplay()
    }
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        // done dragging
        activePath = nil
    }
    
}

和一个示例 controller 定义了一些示例形状(路径):

class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = .systemBackground
        
        let testBezDrawView = BezDrawView()
        testBezDrawView.backgroundColor = UIColor(white: 0.1, alpha: 1.0)
        testBezDrawView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(testBezDrawView)
        
        let g = view.safeAreaLayoutGuide
        
        NSLayoutConstraint.activate([
            
            // constrain bez draw view to all 4 sides with 20-points "padding"
            testBezDrawView.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
            testBezDrawView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
            testBezDrawView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
            testBezDrawView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -20.0),
            
        ])
        
        // add some sample path shapes and colors
        let colors: [UIColor] = [
            .systemRed, .systemGreen, .systemBlue,
            .cyan, .magenta, .yellow, .orange, .green,
        ]
        let rects: [CGRect] = [
            CGRect(x: 20, y: 20, width: 60, height: 60),
            CGRect(x: 180, y: 20, width: 40, height: 60),
            CGRect(x: 20, y: 120, width: 60, height: 100),
            CGRect(x: 200, y: 140, width: 50, height: 90),
            CGRect(x: 90, y: 220, width: 100, height: 60),
            CGRect(x: 220, y: 260, width: 80, height: 160),
            CGRect(x: 50, y: 360, width: 200, height: 100),
            CGRect(x: 150, y: 480, width: 120, height: 80),
        ]
        var somePaths: [MyPath] = []
        var i: Int = 0
        for (c, r) in zip(colors, rects) {
            var b = UIBezierPath()
            switch i % 4 {
            case 1:     // oval
                b = UIBezierPath(ovalIn: r)
            case 2:     // triangle shape
                b = UIBezierPath()
                b.move(to: CGPoint(x: r.minX, y: r.maxY))
                b.addLine(to: CGPoint(x: r.midX, y: r.minY))
                b.addLine(to: CGPoint(x: r.maxX, y: r.maxY))
                b.close()
            case 3:     // diamond
                b = UIBezierPath()
                b.move(to: CGPoint(x: r.minX, y: r.midY))
                b.addLine(to: CGPoint(x: r.midX, y: r.minY))
                b.addLine(to: CGPoint(x: r.maxX, y: r.midY))
                b.addLine(to: CGPoint(x: r.midX, y: r.maxY))
                b.close()
            default:    // rect
                b = UIBezierPath(rect: r)
            }
            b.lineWidth = 4
            b.lineCapStyle = .round
            b.lineJoinStyle = .round
            b.setLineDash([5, 10], count: 2, phase: 0)
            let p = MyPath(color: c, path: b)
            somePaths.append(p)
            i += 1
        }
        testBezDrawView.myPaths = somePaths
    }
    
}

这是使用中的视频(太大无法转换为 gif 并嵌入此处):

https://imgur.com/a/BmaHkKM

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM