我有下面的代码,使用UIBezierPath绘制线条.
代码使用addCurveToPoint
哪个应使用三次贝塞尔曲线路径绘制曲线,但代码的最终结果是绘制连接的直线但未addLineToPoint
使用.
可能会发生什么,为什么不是代码绘制曲线?
import UIKit class DrawingView: UIView, UITextFieldDelegate { // Modifiable values within the code let lineWidth : CGFloat = 2.0 let lineColor = UIColor.redColor() let lineColorAlpha : CGFloat = 0.4 let shouldAllowUserChangeLineWidth = true let maximumUndoRedoChances = 10 var path = UIBezierPath() var previousImages : [UIImage] = [UIImage]() // Represents current image index var currentImageIndex = 0 // Control points for drawing curve smoothly private var controlPoint1 : CGPoint? private var controlPoint2 : CGPoint? private var undoButton : UIButton! private var redoButton : UIButton! private var textField : UITextField! //MARK: Init methods override init(frame: CGRect) { super.init(frame: frame) setDefaultValues() } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) setDefaultValues() } // Draw the path when needed override func drawRect(rect: CGRect) { if currentImageIndex > 0 { previousImages[currentImageIndex - 1].drawInRect(rect) } lineColor.setStroke() path.strokeWithBlendMode(CGBlendMode.Normal, alpha: lineColorAlpha) } override func layoutSubviews() { super.layoutSubviews() redoButton.frame = CGRectMake(bounds.size.width - 58, 30, 50, 44) if shouldAllowUserChangeLineWidth { textField.center = CGPointMake(center.x, 52) } } func setDefaultValues() { multipleTouchEnabled = false backgroundColor = UIColor.whiteColor() path.lineWidth = lineWidth addButtonsAndField() } func addButtonsAndField() { undoButton = UIButton(frame: CGRectMake(8, 30, 50, 44)) undoButton.setTitle("Undo", forState: UIControlState.Normal) undoButton.setTitleColor(UIColor.blackColor(), forState: UIControlState.Normal) undoButton.backgroundColor = UIColor.lightGrayColor() undoButton.addTarget(self, action: "undoButtonTapped:", forControlEvents: UIControlEvents.TouchUpInside) addSubview(undoButton) redoButton = UIButton(frame: CGRectMake(bounds.size.width - 58, 30, 50, 44)) redoButton.setTitle("Redo", forState: UIControlState.Normal) redoButton.setTitleColor(UIColor.blackColor(), forState: UIControlState.Normal) redoButton.backgroundColor = UIColor.lightGrayColor() redoButton.addTarget(self, action: "redoButtonTapped:", forControlEvents: UIControlEvents.TouchUpInside) addSubview(redoButton) if shouldAllowUserChangeLineWidth { textField = UITextField(frame: CGRectMake(0, 0, 50, 40)) textField.backgroundColor = UIColor.lightGrayColor() textField.center = CGPointMake(center.x, 52) textField.keyboardType = UIKeyboardType.NumberPad textField.delegate = self addSubview(textField) } } //MARK: Touches methods override func touchesBegan(touches: Set, withEvent event: UIEvent?) { // Find the start point and move the path there endEditing(true) let touchPoint = touches.first?.locationInView(self) path.moveToPoint(touchPoint!) } override func touchesMoved(touches: Set , withEvent event: UIEvent?) { let touchPoint = touches.first?.locationInView(self) controlPoint1 = CGPointMake((path.currentPoint.x + touchPoint!.x) / 2, (path.currentPoint.y + touchPoint!.y) / 2) controlPoint2 = CGPointMake((path.currentPoint.x + touchPoint!.x) / 2, (path.currentPoint.y + touchPoint!.y) / 2) path.addCurveToPoint(touchPoint!, controlPoint1: controlPoint1!, controlPoint2: controlPoint2!) setNeedsDisplay() } override func touchesEnded(touches: Set , withEvent event: UIEvent?) { let touchPoint = touches.first?.locationInView(self) controlPoint1 = CGPointMake((path.currentPoint.x + touchPoint!.x) / 2, (path.currentPoint.y + touchPoint!.y) / 2) controlPoint2 = CGPointMake((path.currentPoint.x + touchPoint!.x) / 2, (path.currentPoint.y + touchPoint!.y) / 2) path.addCurveToPoint(touchPoint!, controlPoint1: controlPoint1!, controlPoint2: controlPoint2!) savePreviousImage() setNeedsDisplay() // Remove all points to optimize the drawing speed path.removeAllPoints() } override func touchesCancelled(touches: Set ?, withEvent event: UIEvent?) { touchesEnded(touches!, withEvent: event) } //MARK: Selector methods func undoButtonTapped(sender : UIButton) { if currentImageIndex > 0 { setNeedsDisplay() currentImageIndex-- } } func redoButtonTapped(sender : UIButton) { if currentImageIndex != previousImages.count { setNeedsDisplay() currentImageIndex++ } } //MARK: UITextFieldDelegate func textFieldDidEndEditing(textField: UITextField) { if let n = NSNumberFormatter().numberFromString(textField.text!) { if n.integerValue > 0 { path.lineWidth = CGFloat(n) } } } //MARK: Saving images for reloading when undo or redo called private func savePreviousImage() { UIGraphicsBeginImageContextWithOptions(bounds.size, true, UIScreen.mainScreen().scale) lineColor.setStroke() // Create a image with white color let rectPath = UIBezierPath(rect: bounds) backgroundColor?.setFill() rectPath.fill() if currentImageIndex > 0 { previousImages[currentImageIndex - 1].drawInRect(bounds) } path.strokeWithBlendMode(CGBlendMode.Normal, alpha: lineColorAlpha) if previousImages.count >= currentImageIndex { previousImages.removeRange(currentImageIndex.. = maximumUndoRedoChances { previousImages.removeFirst() } else { currentImageIndex++ } previousImages.append(UIGraphicsGetImageFromCurrentImageContext()) UIGraphicsEndImageContext() } }
Rob.. 23
有几个问题:
您正在使用两点之间中点的控制点,从而产生线段.您可能希望选择平滑曲线的控制点.请参见http://spin.atomicobject.com/2014/05/28/ios-interpolating-points/.
这是一个简单平滑算法的Swift 3实现,以及上面的Hermite和Catmull-Rom Spline方法的Swift演绎:
extension UIBezierPath { /// Simple smoothing algorithm /// /// This iterates through the points in the array, drawing cubic bezier /// from the first to the fourth points, using the second and third as /// control points. /// /// This takes every third point and moves it so that it is exactly inbetween /// the points before and after it, which ensures that there is no discontinuity /// in the first derivative as you join these cubic beziers together. /// /// Note, if, at the end, there are not enough points for a cubic bezier, it /// will perform a quadratic bezier, or if not enough points for that, a line. /// /// - parameter points: The array of `CGPoint`. convenience init?(simpleSmooth points: [CGPoint]) { guard points.count > 1 else { return nil } self.init() move(to: points[0]) var index = 0 while index < (points.count - 1) { switch (points.count - index) { case 2: index += 1 addLine(to: points[index]) case 3: index += 2 addQuadCurve(to: points[index], controlPoint: points[index-1]) case 4: index += 3 addCurve(to: points[index], controlPoint1: points[index-2], controlPoint2: points[index-1]) default: index += 3 let point = CGPoint(x: (points[index-1].x + points[index+1].x) / 2, y: (points[index-1].y + points[index+1].y) / 2) addCurve(to: point, controlPoint1: points[index-2], controlPoint2: points[index-1]) } } } /// Create smooth UIBezierPath using Hermite Spline /// /// This requires at least two points. /// /// Adapted from https://github.com/jnfisher/ios-curve-interpolation /// See http://spin.atomicobject.com/2014/05/28/ios-interpolating-points/ /// /// - parameter hermiteInterpolatedPoints: The array of CGPoint values. /// - parameter closed: Whether the path should be closed or not /// /// - returns: An initialized `UIBezierPath`, or `nil` if an object could not be created for some reason (e.g. not enough points). convenience init?(hermiteInterpolatedPoints points: [CGPoint], closed: Bool) { self.init() guard points.count > 1 else { return nil } let numberOfCurves = closed ? points.count : points.count - 1 var previousPoint: CGPoint? = closed ? points.last : nil var currentPoint: CGPoint = points[0] var nextPoint: CGPoint? = points[1] move(to: currentPoint) for index in 0 ..< numberOfCurves { let endPt = nextPoint! var mx: CGFloat var my: CGFloat if previousPoint != nil { mx = (nextPoint!.x - currentPoint.x) * 0.5 + (currentPoint.x - previousPoint!.x)*0.5 my = (nextPoint!.y - currentPoint.y) * 0.5 + (currentPoint.y - previousPoint!.y)*0.5 } else { mx = (nextPoint!.x - currentPoint.x) * 0.5 my = (nextPoint!.y - currentPoint.y) * 0.5 } let ctrlPt1 = CGPoint(x: currentPoint.x + mx / 3.0, y: currentPoint.y + my / 3.0) previousPoint = currentPoint currentPoint = nextPoint! let nextIndex = index + 2 if closed { nextPoint = points[nextIndex % points.count] } else { nextPoint = nextIndex < points.count ? points[nextIndex % points.count] : nil } if nextPoint != nil { mx = (nextPoint!.x - currentPoint.x) * 0.5 + (currentPoint.x - previousPoint!.x) * 0.5 my = (nextPoint!.y - currentPoint.y) * 0.5 + (currentPoint.y - previousPoint!.y) * 0.5 } else { mx = (currentPoint.x - previousPoint!.x) * 0.5 my = (currentPoint.y - previousPoint!.y) * 0.5 } let ctrlPt2 = CGPoint(x: currentPoint.x - mx / 3.0, y: currentPoint.y - my / 3.0) addCurve(to: endPt, controlPoint1: ctrlPt1, controlPoint2: ctrlPt2) } if closed { close() } } /// Create smooth UIBezierPath using Catmull-Rom Splines /// /// This requires at least four points. /// /// Adapted from https://github.com/jnfisher/ios-curve-interpolation /// See http://spin.atomicobject.com/2014/05/28/ios-interpolating-points/ /// /// - parameter catmullRomInterpolatedPoints: The array of CGPoint values. /// - parameter closed: Whether the path should be closed or not /// - parameter alpha: The alpha factor to be applied to Catmull-Rom spline. /// /// - returns: An initialized `UIBezierPath`, or `nil` if an object could not be created for some reason (e.g. not enough points). convenience init?(catmullRomInterpolatedPoints points: [CGPoint], closed: Bool, alpha: CGFloat) { self.init() guard points.count > 3 else { return nil } assert(alpha >= 0 && alpha <= 1.0, "Alpha must be between 0 and 1") let endIndex = closed ? points.count : points.count - 2 let startIndex = closed ? 0 : 1 let kEPSILON: CGFloat = 1.0e-5 move(to: points[startIndex]) for index in startIndex ..< endIndex { let nextIndex = (index + 1) % points.count let nextNextIndex = (nextIndex + 1) % points.count let previousIndex = index < 1 ? points.count - 1 : index - 1 let point0 = points[previousIndex] let point1 = points[index] let point2 = points[nextIndex] let point3 = points[nextNextIndex] let d1 = hypot(CGFloat(point1.x - point0.x), CGFloat(point1.y - point0.y)) let d2 = hypot(CGFloat(point2.x - point1.x), CGFloat(point2.y - point1.y)) let d3 = hypot(CGFloat(point3.x - point2.x), CGFloat(point3.y - point2.y)) let d1a2 = pow(d1, alpha * 2) let d1a = pow(d1, alpha) let d2a2 = pow(d2, alpha * 2) let d2a = pow(d2, alpha) let d3a2 = pow(d3, alpha * 2) let d3a = pow(d3, alpha) var controlPoint1: CGPoint, controlPoint2: CGPoint if abs(d1) < kEPSILON { controlPoint1 = point2 } else { controlPoint1 = (point2 * d1a2 - point0 * d2a2 + point1 * (2 * d1a2 + 3 * d1a * d2a + d2a2)) / (3 * d1a * (d1a + d2a)) } if abs(d3) < kEPSILON { controlPoint2 = point2 } else { controlPoint2 = (point1 * d3a2 - point3 * d2a2 + point2 * (2 * d3a2 + 3 * d3a * d2a + d2a2)) / (3 * d3a * (d3a + d2a)) } addCurve(to: point2, controlPoint1: controlPoint1, controlPoint2: controlPoint2) } if closed { close() } } } // Some functions to make the Catmull-Rom splice code a little more readable. // These multiply/divide a `CGPoint` by a scalar and add/subtract one `CGPoint` // from another. func * (lhs: CGPoint, rhs: CGFloat) -> CGPoint { return CGPoint(x: lhs.x * rhs, y: lhs.y * CGFloat(rhs)) } func / (lhs: CGPoint, rhs: CGFloat) -> CGPoint { return CGPoint(x: lhs.x / rhs, y: lhs.y / CGFloat(rhs)) } func + (lhs: CGPoint, rhs: CGPoint) -> CGPoint { return CGPoint(x: lhs.x + rhs.x, y: lhs.y + rhs.y) } func - (lhs: CGPoint, rhs: CGPoint) -> CGPoint { return CGPoint(x: lhs.x - rhs.x, y: lhs.y - rhs.y) }
以下是分别为红色,蓝色和绿色的"简单"平滑算法"Hermite"样条和"Catmull Rom"样条曲线.正如您所看到的,"简单"平滑算法在计算上更简单,但通常不会通过许多点(但提供更加戏剧性的平滑,消除了笔划中的任何不稳定性).像这样跳跃的点夸大了行为,而在标准的"手势"中,它提供了相当不错的平滑效果.另一方面,样条曲线在穿过阵列中的点时平滑曲线.
如果定位iOS 9及更高版本,它会引入一些不错的功能,特别是:
如果用户正在使用具有此类功能的设备(尤其是较新的iPad),则会合并触摸.最重要的是,这些设备(但不是它们的模拟器)能够每秒产生超过60次触摸,因此您可以为每次呼叫报告多次触摸touchesMoved
.
预测触摸,设备可以显示预期用户触摸的位置将进展(导致绘图中的延迟更少).
把它们拉到一起,你可能会做类似的事情:
var points: [CGPoint]? var path: UIBezierPath? override func touchesBegan(_ touches: Set, with event: UIEvent?) { if let touch = touches.first { points = [touch.location(in: view)] } } override func touchesMoved(_ touches: Set , with event: UIEvent?) { if let touch = touches.first { if #available(iOS 9.0, *) { if let coalescedTouches = event?.coalescedTouches(for: touch) { points? += coalescedTouches.map { $0.location(in: view) } } else { points?.append(touch.location(in: view)) } if let predictedTouches = event?.predictedTouches(for: touch) { let predictedPoints = predictedTouches.map { $0.location(in: view) } pathLayer.path = UIBezierPath(catmullRomInterpolatedPoints: points! + predictedPoints, closed: false, alpha: 0.5)?.cgPath } else { pathLayer.path = UIBezierPath(catmullRomInterpolatedPoints: points!, closed: false, alpha: 0.5)?.cgPath } } else { points?.append(touch.location(in: view)) pathLayer.path = UIBezierPath(catmullRomInterpolatedPoints: points!, closed: false, alpha: 0.5)?.cgPath } } } override func touchesEnded(_ touches: Set , with event: UIEvent?) { path = UIBezierPath(catmullRomInterpolatedPoints: points!, closed: false, alpha: 0.5) pathLayer.path = path?.cgPath }
在这段代码片段中,我通过更新a CAShapeLayer
来渲染路径,但是如果你想以其他方式渲染它,请随意.例如,使用您的drawRect
方法,您将更新path
,然后调用setNeedsDisplay()
.
并且,上面说明了if #available(iOS 9, *) { ... } else { ... }
如果您需要支持9.0之前的iOS版本的语法,但很明显,如果您只支持iOS 9及更高版本,则可以删除该检查并丢失该else
子句.
有关更多信息,请参阅iOS上的 WWDC 2015视频高级触摸输入.
无论如何,这产生如下:
(对于上面的Swift 2.3演绎,请参阅此答案的先前版本.)
有几个问题:
您正在使用两点之间中点的控制点,从而产生线段.您可能希望选择平滑曲线的控制点.请参见http://spin.atomicobject.com/2014/05/28/ios-interpolating-points/.
这是一个简单平滑算法的Swift 3实现,以及上面的Hermite和Catmull-Rom Spline方法的Swift演绎:
extension UIBezierPath { /// Simple smoothing algorithm /// /// This iterates through the points in the array, drawing cubic bezier /// from the first to the fourth points, using the second and third as /// control points. /// /// This takes every third point and moves it so that it is exactly inbetween /// the points before and after it, which ensures that there is no discontinuity /// in the first derivative as you join these cubic beziers together. /// /// Note, if, at the end, there are not enough points for a cubic bezier, it /// will perform a quadratic bezier, or if not enough points for that, a line. /// /// - parameter points: The array of `CGPoint`. convenience init?(simpleSmooth points: [CGPoint]) { guard points.count > 1 else { return nil } self.init() move(to: points[0]) var index = 0 while index < (points.count - 1) { switch (points.count - index) { case 2: index += 1 addLine(to: points[index]) case 3: index += 2 addQuadCurve(to: points[index], controlPoint: points[index-1]) case 4: index += 3 addCurve(to: points[index], controlPoint1: points[index-2], controlPoint2: points[index-1]) default: index += 3 let point = CGPoint(x: (points[index-1].x + points[index+1].x) / 2, y: (points[index-1].y + points[index+1].y) / 2) addCurve(to: point, controlPoint1: points[index-2], controlPoint2: points[index-1]) } } } /// Create smooth UIBezierPath using Hermite Spline /// /// This requires at least two points. /// /// Adapted from https://github.com/jnfisher/ios-curve-interpolation /// See http://spin.atomicobject.com/2014/05/28/ios-interpolating-points/ /// /// - parameter hermiteInterpolatedPoints: The array of CGPoint values. /// - parameter closed: Whether the path should be closed or not /// /// - returns: An initialized `UIBezierPath`, or `nil` if an object could not be created for some reason (e.g. not enough points). convenience init?(hermiteInterpolatedPoints points: [CGPoint], closed: Bool) { self.init() guard points.count > 1 else { return nil } let numberOfCurves = closed ? points.count : points.count - 1 var previousPoint: CGPoint? = closed ? points.last : nil var currentPoint: CGPoint = points[0] var nextPoint: CGPoint? = points[1] move(to: currentPoint) for index in 0 ..< numberOfCurves { let endPt = nextPoint! var mx: CGFloat var my: CGFloat if previousPoint != nil { mx = (nextPoint!.x - currentPoint.x) * 0.5 + (currentPoint.x - previousPoint!.x)*0.5 my = (nextPoint!.y - currentPoint.y) * 0.5 + (currentPoint.y - previousPoint!.y)*0.5 } else { mx = (nextPoint!.x - currentPoint.x) * 0.5 my = (nextPoint!.y - currentPoint.y) * 0.5 } let ctrlPt1 = CGPoint(x: currentPoint.x + mx / 3.0, y: currentPoint.y + my / 3.0) previousPoint = currentPoint currentPoint = nextPoint! let nextIndex = index + 2 if closed { nextPoint = points[nextIndex % points.count] } else { nextPoint = nextIndex < points.count ? points[nextIndex % points.count] : nil } if nextPoint != nil { mx = (nextPoint!.x - currentPoint.x) * 0.5 + (currentPoint.x - previousPoint!.x) * 0.5 my = (nextPoint!.y - currentPoint.y) * 0.5 + (currentPoint.y - previousPoint!.y) * 0.5 } else { mx = (currentPoint.x - previousPoint!.x) * 0.5 my = (currentPoint.y - previousPoint!.y) * 0.5 } let ctrlPt2 = CGPoint(x: currentPoint.x - mx / 3.0, y: currentPoint.y - my / 3.0) addCurve(to: endPt, controlPoint1: ctrlPt1, controlPoint2: ctrlPt2) } if closed { close() } } /// Create smooth UIBezierPath using Catmull-Rom Splines /// /// This requires at least four points. /// /// Adapted from https://github.com/jnfisher/ios-curve-interpolation /// See http://spin.atomicobject.com/2014/05/28/ios-interpolating-points/ /// /// - parameter catmullRomInterpolatedPoints: The array of CGPoint values. /// - parameter closed: Whether the path should be closed or not /// - parameter alpha: The alpha factor to be applied to Catmull-Rom spline. /// /// - returns: An initialized `UIBezierPath`, or `nil` if an object could not be created for some reason (e.g. not enough points). convenience init?(catmullRomInterpolatedPoints points: [CGPoint], closed: Bool, alpha: CGFloat) { self.init() guard points.count > 3 else { return nil } assert(alpha >= 0 && alpha <= 1.0, "Alpha must be between 0 and 1") let endIndex = closed ? points.count : points.count - 2 let startIndex = closed ? 0 : 1 let kEPSILON: CGFloat = 1.0e-5 move(to: points[startIndex]) for index in startIndex ..< endIndex { let nextIndex = (index + 1) % points.count let nextNextIndex = (nextIndex + 1) % points.count let previousIndex = index < 1 ? points.count - 1 : index - 1 let point0 = points[previousIndex] let point1 = points[index] let point2 = points[nextIndex] let point3 = points[nextNextIndex] let d1 = hypot(CGFloat(point1.x - point0.x), CGFloat(point1.y - point0.y)) let d2 = hypot(CGFloat(point2.x - point1.x), CGFloat(point2.y - point1.y)) let d3 = hypot(CGFloat(point3.x - point2.x), CGFloat(point3.y - point2.y)) let d1a2 = pow(d1, alpha * 2) let d1a = pow(d1, alpha) let d2a2 = pow(d2, alpha * 2) let d2a = pow(d2, alpha) let d3a2 = pow(d3, alpha * 2) let d3a = pow(d3, alpha) var controlPoint1: CGPoint, controlPoint2: CGPoint if abs(d1) < kEPSILON { controlPoint1 = point2 } else { controlPoint1 = (point2 * d1a2 - point0 * d2a2 + point1 * (2 * d1a2 + 3 * d1a * d2a + d2a2)) / (3 * d1a * (d1a + d2a)) } if abs(d3) < kEPSILON { controlPoint2 = point2 } else { controlPoint2 = (point1 * d3a2 - point3 * d2a2 + point2 * (2 * d3a2 + 3 * d3a * d2a + d2a2)) / (3 * d3a * (d3a + d2a)) } addCurve(to: point2, controlPoint1: controlPoint1, controlPoint2: controlPoint2) } if closed { close() } } } // Some functions to make the Catmull-Rom splice code a little more readable. // These multiply/divide a `CGPoint` by a scalar and add/subtract one `CGPoint` // from another. func * (lhs: CGPoint, rhs: CGFloat) -> CGPoint { return CGPoint(x: lhs.x * rhs, y: lhs.y * CGFloat(rhs)) } func / (lhs: CGPoint, rhs: CGFloat) -> CGPoint { return CGPoint(x: lhs.x / rhs, y: lhs.y / CGFloat(rhs)) } func + (lhs: CGPoint, rhs: CGPoint) -> CGPoint { return CGPoint(x: lhs.x + rhs.x, y: lhs.y + rhs.y) } func - (lhs: CGPoint, rhs: CGPoint) -> CGPoint { return CGPoint(x: lhs.x - rhs.x, y: lhs.y - rhs.y) }
以下是分别为红色,蓝色和绿色的"简单"平滑算法"Hermite"样条和"Catmull Rom"样条曲线.正如您所看到的,"简单"平滑算法在计算上更简单,但通常不会通过许多点(但提供更加戏剧性的平滑,消除了笔划中的任何不稳定性).像这样跳跃的点夸大了行为,而在标准的"手势"中,它提供了相当不错的平滑效果.另一方面,样条曲线在穿过阵列中的点时平滑曲线.
如果定位iOS 9及更高版本,它会引入一些不错的功能,特别是:
如果用户正在使用具有此类功能的设备(尤其是较新的iPad),则会合并触摸.最重要的是,这些设备(但不是它们的模拟器)能够每秒产生超过60次触摸,因此您可以为每次呼叫报告多次触摸touchesMoved
.
预测触摸,设备可以显示预期用户触摸的位置将进展(导致绘图中的延迟更少).
把它们拉到一起,你可能会做类似的事情:
var points: [CGPoint]? var path: UIBezierPath? override func touchesBegan(_ touches: Set, with event: UIEvent?) { if let touch = touches.first { points = [touch.location(in: view)] } } override func touchesMoved(_ touches: Set , with event: UIEvent?) { if let touch = touches.first { if #available(iOS 9.0, *) { if let coalescedTouches = event?.coalescedTouches(for: touch) { points? += coalescedTouches.map { $0.location(in: view) } } else { points?.append(touch.location(in: view)) } if let predictedTouches = event?.predictedTouches(for: touch) { let predictedPoints = predictedTouches.map { $0.location(in: view) } pathLayer.path = UIBezierPath(catmullRomInterpolatedPoints: points! + predictedPoints, closed: false, alpha: 0.5)?.cgPath } else { pathLayer.path = UIBezierPath(catmullRomInterpolatedPoints: points!, closed: false, alpha: 0.5)?.cgPath } } else { points?.append(touch.location(in: view)) pathLayer.path = UIBezierPath(catmullRomInterpolatedPoints: points!, closed: false, alpha: 0.5)?.cgPath } } } override func touchesEnded(_ touches: Set , with event: UIEvent?) { path = UIBezierPath(catmullRomInterpolatedPoints: points!, closed: false, alpha: 0.5) pathLayer.path = path?.cgPath }
在这段代码片段中,我通过更新a CAShapeLayer
来渲染路径,但是如果你想以其他方式渲染它,请随意.例如,使用您的drawRect
方法,您将更新path
,然后调用setNeedsDisplay()
.
并且,上面说明了if #available(iOS 9, *) { ... } else { ... }
如果您需要支持9.0之前的iOS版本的语法,但很明显,如果您只支持iOS 9及更高版本,则可以删除该检查并丢失该else
子句.
有关更多信息,请参阅iOS上的 WWDC 2015视频高级触摸输入.
无论如何,这产生如下:
(对于上面的Swift 2.3演绎,请参阅此答案的先前版本.)