Removing lagging latency during continuous period of drawing UIBezierPath in Swift
下面的代码通过覆盖触摸来绘制线条,但是在连续不间断的绘制期间开始出现滞后。手指在屏幕上移动的时间越长,这种滞后就会越积越多。结果是实际设备上的 CPU 几乎达到最大值(CPU 98% ),并且绘制的时间越长,生成的图像就越不稳定。
此外,当画得特别快时,尤其是在圆圈中,在
1 - 如何消除连续绘制一段时间的滞后延迟?
2 - 如何消除绘制路径的差异?
3 - 如何更改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | class swiftView: UIView { var strokeColor = UIColor.blueColor() var lineWidth: CGFloat = 5 var snapshotImage: UIImage? private var path: UIBezierPath? private var temporaryPath: UIBezierPath? private var points = [CGPoint]() var counterPoints:Int? required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } override func drawRect(rect: CGRect) { autoreleasepool { snapshotImage?.drawInRect(rect) strokeColor.setStroke() path?.stroke() temporaryPath?.stroke() } } override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) { let touch: AnyObject? = touches.first points = [touch!.locationInView(self)] counterPoints = 0 } override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) { let touch: AnyObject? = touches.first let point = touch!.locationInView(self) points.append(point) let pointCount = points.count counterPoints = counterPoints! + 1 if pointCount == 2 { temporaryPath = createPathStartingAtPoint(points[0]) temporaryPath?.addLineToPoint(points[1]) setNeedsDisplay() } else if pointCount == 3 { temporaryPath = createPathStartingAtPoint(points[0]) temporaryPath?.addQuadCurveToPoint(points[2], controlPoint: points[1]) setNeedsDisplay() } else if pointCount == 4 { temporaryPath = createPathStartingAtPoint(points[0]) temporaryPath?.addCurveToPoint(points[3], controlPoint1: points[1], controlPoint2: points[2]) // setNeedsDisplay() if counterPoints! < 50 { self.setNeedsDisplay() } else { temporaryPath = nil self.constructIncrementalImage() path = nil self.setNeedsDisplay() counterPoints = 0 } } else if pointCount == 5 { points[3] = CGPointMake((points[2].x + points[4].x)/2.0, (points[2].y + points[4].y)/2.0) // create a quad bezier up to point 4, too if points[4] != points[3] { let length = hypot(points[4].x - points[3].x, points[4].y - points[3].y) / 2.0 let angle = atan2(points[3].y - points[2].y, points[4].x - points[3].x) let controlPoint = CGPoint(x: points[3].x + cos(angle) * length, y: points[3].y + sin(angle) * length) temporaryPath = createPathStartingAtPoint(points[3]) temporaryPath?.addQuadCurveToPoint(points[4], controlPoint: controlPoint) } else { temporaryPath = nil } if path == nil { path = createPathStartingAtPoint(points[0]) } path?.addCurveToPoint(points[3], controlPoint1: points[1], controlPoint2: points[2]) self.setNeedsDisplay() points = [points[3], points[4]] } } override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) { self.constructIncrementalImage() path = nil self.setNeedsDisplay() counterPoints = 0 } override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) { self.touchesEnded(touches!, withEvent: event) } private func createPathStartingAtPoint(point: CGPoint) -> UIBezierPath { let localPath = UIBezierPath() localPath.moveToPoint(point) localPath.lineWidth = lineWidth localPath.lineCapStyle = .Round localPath.lineJoinStyle = .Round return localPath } private func constructIncrementalImage() { UIGraphicsBeginImageContextWithOptions(self.bounds.size, false, 0.0) strokeColor.setStroke() snapshotImage?.drawAtPoint(CGPointZero) path?.stroke() temporaryPath?.stroke() snapshotImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() } } |
你问过:
How can the lagging latency over a period of continuous drawing be eliminated?
正如您正确推测的那样,是的,创建快照并重置路径可以通过限制路径的长度来解决此问题。
我知道您已经意识到这一点,但为了其他读者的利益,在 iOS 9 中您也可以使用预测性触控。在这个特定的算法中(其中(a)您只是添加到路径,但(b)每四个点根据下一个点进行调整,以确保两条三次贝塞尔曲线连接处没有不连续性)有点棘手,但可以做到。
How can the discrepancy in the paths drawn be eliminated?
这是因为快照包含临时路径。但是该临时路径的全部目的是随着更多点的进入,它将被丢弃。因此,您不应将其包含在您创建的中间手势快照中。
所以,我建议在快照函数中添加一个参数,指示是否应包含
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | class SmoothCurvedLinesView: UIView { var strokeColor = UIColor.blueColor() var lineWidth: CGFloat = 20 var snapshotImage: UIImage? private var path: UIBezierPath? private var temporaryPath: UIBezierPath? private var points = [CGPoint]() private var totalPointCount = 0 override func drawRect(rect: CGRect) { snapshotImage?.drawInRect(rect) strokeColor.setStroke() path?.stroke() temporaryPath?.stroke() } override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) { let touch: AnyObject? = touches.first points = [touch!.locationInView(self)] totalPointCount = totalPointCount + 1 } override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) { let touch: AnyObject? = touches.first let point = touch!.locationInView(self) points.append(point) totalPointCount = totalPointCount + 1 updatePaths() if totalPointCount > 50 { constructIncrementalImage(includeTemporaryPath: false) path = nil totalPointCount = 0 } setNeedsDisplay() } private func updatePaths() { // update main path while points.count > 4 { points[3] = CGPointMake((points[2].x + points[4].x)/2.0, (points[2].y + points[4].y)/2.0) if path == nil { path = createPathStartingAtPoint(points[0]) } path?.addCurveToPoint(points[3], controlPoint1: points[1], controlPoint2: points[2]) points.removeFirst(3) } // build temporary path up to last touch point let pointCount = points.count if pointCount == 2 { temporaryPath = createPathStartingAtPoint(points[0]) temporaryPath?.addLineToPoint(points[1]) } else if pointCount == 3 { temporaryPath = createPathStartingAtPoint(points[0]) temporaryPath?.addQuadCurveToPoint(points[2], controlPoint: points[1]) } else if pointCount == 4 { temporaryPath = createPathStartingAtPoint(points[0]) temporaryPath?.addCurveToPoint(points[3], controlPoint1: points[1], controlPoint2: points[2]) } } override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) { constructIncrementalImage() path = nil temporaryPath = nil setNeedsDisplay() } override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) { touchesEnded(touches!, withEvent: event) } private func createPathStartingAtPoint(point: CGPoint) -> UIBezierPath { let localPath = UIBezierPath() localPath.moveToPoint(point) localPath.lineWidth = lineWidth localPath.lineCapStyle = .Round localPath.lineJoinStyle = .Round return localPath } private func constructIncrementalImage(includeTemporaryPath includeTemporaryPath: Bool = true) { UIGraphicsBeginImageContextWithOptions(bounds.size, false, 0.0) strokeColor.setStroke() snapshotImage?.drawAtPoint(CGPointZero) path?.stroke() if (includeTemporaryPath) { temporaryPath?.stroke() } snapshotImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() } } |
顺便说一句,虽然我是提供路径生成代码的人,但我意识到它可以简化一点。我也修复了一个错误。见上面的代码。
然后你问:
How can the alpha/opacity of the path and temporaryPath be changed?
您可以使用适当的 alpha 调整调用
1 2 3 4 5 6 7 8 9 | override func drawRect(rect: CGRect) { snapshotImage?.drawInRect(rect) strokeColor.setStroke() path?.stroke() strokeColor.colorWithAlphaComponent(0.5).setStroke() temporaryPath?.stroke() } |