[英]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 并嵌入此处):
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.