在Swift中绘制人造物

下面的代码通过覆盖触摸来绘制线条,但是在绘制时存在一个人造物,见下图。

当改变方向而锯齿形在屏幕上蜿蜒的时候,有时线会变成一个平坦的直angular,而不是保持圆形。 当在小圆点上画点时也会出现人为现象,当手指离开屏幕时,绘图点有时会半圈闪烁半圈,部分圈残留。

人工制品是间歇性的,而不是完全一致或可预测的模式,这使得很难在代码中find问题。 它出现在模拟器和iOS7 – iOS9的设备上。

一个包含两个带有Xcode项目绘制点和线的video屏幕截图的zip文件被上传到一个名为Archive.zip(23MB)的DropBox文件中https://www.dropbox.com/s/hm39rdiuk0mf578/Archive.zip?dl = 0

问题:

1 – 在代码中,是什么造成了这个点/半圆的人造物,怎样才能纠正?

在这里输入图像说明

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 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() } } 

这似乎是addQuadCurveToPointaddCurveToPoint中的一个迷人的错误,如果控制点与两个端点位于同一行,则不符合lineJoinStyle 。 所以你可以testing这个(通过查看各个点的atan2并确保不一样),如果是的话,只需要addLineToPoint

我发现这个修改后的代码删除了这些工件:

 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]) } addCubicCurveToPath(path) 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]) addQuadCurveToPath(temporaryPath) } else if pointCount == 4 { temporaryPath = createPathStartingAtPoint(points[0]) addCubicCurveToPath(temporaryPath) } } /// Add cubic curve to path /// /// Because of bug with bezier curves that fold back on themselves do no honor `lineJoinStyle`, /// check to see if this occurs, and if so, just add lines rather than cubic bezier path. private func addCubicCurveToPath(somePath: UIBezierPath?) { let m01 = atan2(points[0].x - points[1].x, points[0].y - points[1].y) let m23 = atan2(points[2].x - points[3].x, points[2].y - points[3].y) let m03 = atan2(points[0].x - points[3].x, points[0].y - points[3].y) if m01 == m03 || m23 == m03 || points[0] == points[3] { somePath?.addLineToPoint(points[1]) somePath?.addLineToPoint(points[2]) somePath?.addLineToPoint(points[3]) } else { somePath?.addCurveToPoint(points[3], controlPoint1: points[1], controlPoint2: points[2]) } } /// Add quadratic curve to path /// /// Because of bug with bezier curves that fold back on themselves do no honor `lineJoinStyle`, /// check to see if this occurs, and if so, just add lines rather than quadratic bezier path. private func addQuadCurveToPath(somePath: UIBezierPath?) { let m01 = atan2(points[0].x - points[1].x, points[0].y - points[1].y) let m12 = atan2(points[1].x - points[2].x, points[1].y - points[2].y) let m02 = atan2(points[0].x - points[2].x, points[0].y - points[2].y) if m01 == m02 || m12 == m02 || points[0] == points[2] { somePath?.addLineToPoint(points[1]) somePath?.addLineToPoint(points[2]) } else { somePath?.addQuadCurveToPoint(points[2], controlPoint: points[1]) } } 

此外,这可能过于谨慎,但可能谨慎的做法是确保两个连续的点与guard声明绝不相同:

 override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) { let touch: AnyObject? = touches.first let point = touch!.locationInView(self) guard point != points.last else { return } points.append(point) totalPointCount = totalPointCount + 1 updatePaths() if totalPointCount > 50 { constructIncrementalImage(includeTemporaryPath: false) path = nil totalPointCount = 0 } setNeedsDisplay() } 

如果你发现有其他问题,你可以重复我刚刚做的debugging练习。 即运行代码,直到一个问题本身performance出来,而是立即停止并查看points组的日志,看看引起了什么问题,然后创build一个init?(coder:) ,始终如一地将问题转化为100%的时间,例如:

 required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) points.append(CGPoint(x: 239.33332824707, y: 419.0)) points.append(CGPoint(x: 239.33332824707, y: 420.0)) points.append(CGPoint(x: 239.33332824707, y: 419.3)) updatePaths() } 

然后,一再重现的问题,debugging很容易。 所以在诊断出这个问题之后,我修改了updatePaths直到问题解决。 然后我评论了init? 并重复整个演习。