我正在制作可以构建图形的应用程序。 我正在使用UIBezierPath,在我决定使用手势移动和缩放图形之前,一切都很好。
这是我的绘画方式:
var isFirstPoint = true
color.set()
funcPath.lineWidth = width
for i in 0...Int(bounds.size.width * contentScaleFactor) {
let cgX = CGFloat(i) / contentScaleFactor
let funcX = Double ((cgX - bounds.width / 2) / scale)
let funcY = function(funcX) * Double(scale)
guard !funcY.isNaN else { continue }
let cgY = -( CGFloat(funcY) - bounds.height / 2)
if isFirstPoint {
funcPath.move(to: CGPoint(x: cgX, y: cgY))
isFirstPoint = false
} else {
if cgY > max(bounds.size.width, bounds.size.height) * 2 {
isFirstPoint = true
} else {
funcPath.addLine(to: CGPoint(x: cgX, y: cgY) )
}
}
}
funcPath.stroke()
有没有更快的方法呢?
答案 0 :(得分:1)
通常,通过对图层进行变换来完成移动和缩放。查看CAShapeLayer
以保持您的前进方向。当用户手势时,应用变换;然后在手势完成后重新创建路径。在更高级的用法中,您可能会重新计算实际路径,而不仅仅是在用户停止时(例如,他们暂停但不放手),而是重新计算实际路径。
答案 1 :(得分:1)
一些想法:
为每个像素重建路径可能是一个昂贵的过程。降低成本的一种方法是减少渲染的数据点数量。例如,如果您要调整scale
并重建整个路径,则在更新路径中间手势时可能需要使用一些step
因子,并将for
循环替换为:
let step = 4
for i in stride(from: 0, through: Int(bounds.size.width * contentScaleFactor), by: step) {
...
}
然后,手势结束时,您可以使用step
的{{1}}再更新一次路径。
另一种方法是不更新1
并每次都调用scale
,而只是更新setNeedsDisplay
视图。 (这在放大时会导致超快的行为,但在缩小时显然会出现问题,因为不会渲染路径中排除的部分。)
如果要对此进行测试,请确保测试在物理设备上构建的版本(即经过优化)。调试版本及其所有的安全检查,实际上会减慢该过程的速度,而这种过程将在计算强度方面如此突出。
顺便说一句,看起来路径的构建被埋在transform
中。我会将draw(rect:)
的构建与路径的绘制分离,因为是的,每次更新函数时,您可能都想更新并绘制路径,所以您绝对不希望重新构建每次调用UIBezierPath
时的路径。
执行此操作后,可能甚至不需要draw(rect:)
。您可能只需添加一个draw(rect:)
作为视图层的子层,设置该形状层的CAShapeLayer
和strokeColor
,然后更新其strokeWidth
。
答案 2 :(得分:0)
如果您能够将翻译(平移)描述为CGAffineTransform
,则可以使用apply(_:)
一次将其应用于UIBezierPath
。