Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ios/93.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios 用苹果铅笔流畅地画图-有些地方不符合顺序_Ios_Swift_Drawing - Fatal编程技术网

Ios 用苹果铅笔流畅地画图-有些地方不符合顺序

Ios 用苹果铅笔流畅地画图-有些地方不符合顺序,ios,swift,drawing,Ios,Swift,Drawing,根据这个答案,我实现了一个用苹果铅笔绘制的画布视图: 导入基础 导入UIKit 类画布视图:UIView{ 变量点:[CGPoint]? 变量路径:UIBezierPath? var路径层:CAShapeLayer! 覆盖func布局子视图(){ } 覆盖func TouchesBegind(Touchs:Set,带有事件:UIEvent?){ pathLayer=CAShapeLayer() pathLayer.fillColor=UIColor.clear.cgColor pathLayer

根据这个答案,我实现了一个用苹果铅笔绘制的画布视图:

<代码>导入基础 导入UIKit 类画布视图:UIView{ 变量点:[CGPoint]? 变量路径:UIBezierPath? var路径层:CAShapeLayer! 覆盖func布局子视图(){ } 覆盖func TouchesBegind(Touchs:Set,带有事件:UIEvent?){ pathLayer=CAShapeLayer() pathLayer.fillColor=UIColor.clear.cgColor pathLayer.strokeColor=UIColor.red.cgColor pathLayer.lineWidth=3 self.layer.addSublayer(路径层) 如果让触摸=先触摸{ 点=[触摸位置(in:self)] } } 覆盖功能触摸移动(touchs:Set,带有事件:UIEvent?){ 如果让触摸=先触摸{ 如果可用(iOS 9.0,*){ 如果let coalescedTouches=事件?。coalescedTouches(用于:触摸){ 点?+=coalescedTouches.map{$0.location(in:self)} }否则{ 点?.append(触摸位置(in:self)) } 如果let predictedtouchs=event?.predictedtouchs(for:touch){ 让predictedPoints=predictedtouchs.map{$0.location(in:self)} pathLayer.path=UIBezierPath(catmullRomInterpolatedPoints:points!+predictedPoints,closed:false,alpha:0.5)?.cgPath }否则{ pathLayer.path=UIBezierPath(catmullRomInterpolatedPoints:points!,closed:false,alpha:0.5)?.cgPath } }否则{ 点?.append(触摸位置(in:self)) pathLayer.path=UIBezierPath(catmullRomInterpolatedPoints:points!,closed:false,alpha:0.5)?.cgPath } } } 覆盖函数touchesend(touchs:Set,带有事件:UIEvent?){ pathLayer.path=UIBezierPath(catmullRomInterpolatedPoints:points!,closed:false,alpha:0.5)?.cgPath 点?.removeAll() } } 扩展UIBezierPath{ ///简单平滑算法 /// ///这将遍历数组中的点,绘制立方贝塞尔曲线 ///从第一点到第四点,使用第二点和第三点作为 ///控制点。 /// ///这将每三个点移动一次,使其正好处于中间 ///前后的点,确保没有间断 ///在一阶导数中,将这些三次贝塞尔函数连接在一起。 /// ///请注意,如果最后没有足够的点来放置一个三次贝塞尔曲线,它将 ///将执行二次贝塞尔曲线,如果没有足够的点,则执行一条直线。 /// ///-参数点:“CGPoint”的数组。 便利初始化?(简单移动点:[CGPoint]){ guard points.count>1 else{return nil} self.init() 移动(到:点[0]) var指数=0 而指数<(points.count-1){ 开关(points.count-索引){ 案例2: 指数+=1 添加行(到:点[索引]) 案例3: 指数+=2 添加四元曲线(到:点[索引],控制点:点[索引-1]) 案例4: 指数+=3 添加曲线(到:点[索引]、控制点1:点[索引-2]、控制点2:点[索引-1]) 违约: 指数+=3 设点=CGPoint(x:(点[index-1].x+点[index+1].x)/2, y:(点[index-1].y+点[index+1].y)/2) 添加曲线(到:点,控制点1:点[index-2],控制点2:点[index-1]) } } } ///使用Hermite样条线创建平滑的UIBezierPath /// ///这至少需要两点。 /// ///改编自https://github.com/jnfisher/ios-curve-interpolation ///看http://spin.atomicobject.com/2014/05/28/ios-interpolating-points/ /// ///-参数hermiteInterpolatedPoints:CGPoint值的数组。 ///-参数closed:是否关闭路径 /// ///-返回:初始化的'UIBezierPath',如果由于某种原因(例如,没有足够的点)无法创建对象,则返回'nil'。 便利初始?(hermiteInterpolatedPoints:[CGPoint],闭合:布尔){ self.init() guard points.count>1 else{return nil} 设numberOfCurves=闭合?点。计数:点。计数-1 var上一个点:CGPoint?=关闭点。上一个:无 var currentPoint:CGPoint=点[0] var nextPoint:CGPoint?=点[1] 移动(到:当前点) 对于0..import Foundation import UIKit class CanvasView: UIView { var points: [CGPoint]? var path: UIBezierPath? var pathLayer: CAShapeLayer! override func layoutSubviews() { } override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { pathLayer = CAShapeLayer() pathLayer.fillColor = UIColor.clear.cgColor pathLayer.strokeColor = UIColor.red.cgColor pathLayer.lineWidth = 3 self.layer.addSublayer(pathLayer) if let touch = touches.first { points = [touch.location(in: self)] } } override func touchesMoved(_ touches: Set<UITouch>, 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: self) } } else { points?.append(touch.location(in: self)) } if let predictedTouches = event?.predictedTouches(for: touch) { let predictedPoints = predictedTouches.map { $0.location(in: self) } 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: self)) pathLayer.path = UIBezierPath(catmullRomInterpolatedPoints: points!, closed: false, alpha: 0.5)?.cgPath } } } override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { pathLayer.path = UIBezierPath(catmullRomInterpolatedPoints: points!, closed: false, alpha: 0.5)?.cgPath points?.removeAll() } } 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: Float) { 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: Float = 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(Float(point1.x - point0.x), Float(point1.y - point0.y)) let d2 = hypot(Float(point2.x - point1.x), Float(point2.y - point1.y)) let d3 = hypot(Float(point3.x - point2.x), Float(point3.y - point2.y)) let d1a2 = powf(d1, alpha * 2) let d1a = powf(d1, alpha) let d2a2 = powf(d2, alpha * 2) let d2a = powf(d2, alpha) let d3a2 = powf(d3, alpha * 2) let d3a = powf(d3, alpha) var controlPoint1: CGPoint, controlPoint2: CGPoint if fabs(d1) < kEPSILON { controlPoint1 = point2 } else { controlPoint1 = (point2 * d1a2 - point0 * d2a2 + point1 * (2 * d1a2 + 3 * d1a * d2a + d2a2)) / (3 * d1a * (d1a + d2a)) } if fabs(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. private func * (lhs: CGPoint, rhs: Float) -> CGPoint { return CGPoint(x: lhs.x * CGFloat(rhs), y: lhs.y * CGFloat(rhs)) } private func / (lhs: CGPoint, rhs: Float) -> CGPoint { return CGPoint(x: lhs.x / CGFloat(rhs), y: lhs.y / CGFloat(rhs)) } private func + (lhs: CGPoint, rhs: CGPoint) -> CGPoint { return CGPoint(x: lhs.x + rhs.x, y: lhs.y + rhs.y) } private func - (lhs: CGPoint, rhs: CGPoint) -> CGPoint { return CGPoint(x: lhs.x - rhs.x, y: lhs.y - rhs.y) }
import Foundation
import UIKit

class CanvasView: UIView {

var points: [CGPoint]?
var path: UIBezierPath?
var pathLayer: CAShapeLayer!

override func layoutSubviews() {

}

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

    pathLayer = CAShapeLayer()
    pathLayer.fillColor = UIColor.clear.cgColor
    pathLayer.strokeColor = UIColor.red.cgColor
    pathLayer.lineWidth = 1
    pathLayer.lineJoin = kCALineJoinRound
    pathLayer.lineCap = kCALineCapRound
    self.layer.addSublayer(pathLayer)

    if let touch = touches.first {

        points = [touch.location(in: self)]
    }
}

override func touchesMoved(_ touches: Set<UITouch>, 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: self) }
            }
            else {

                points?.append(touch.location(in: self))
            }

            if let predictedTouches = event?.predictedTouches(for: touch) {

                let predictedPoints = predictedTouches.map { $0.location(in: self) }
                pathLayer.path = UIBezierPath.interpolateHermiteFor(points: points! + predictedPoints, closed: false).cgPath
            }
            else {

                pathLayer.path = UIBezierPath.interpolateHermiteFor(points: points!, closed: false).cgPath
            }
        }
        else {

            points?.append(touch.location(in: self))
            pathLayer.path = UIBezierPath.interpolateHermiteFor(points: points!, closed: false).cgPath
        }
    }
}

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {

    pathLayer.path = UIBezierPath.interpolateHermiteFor(points: points!, closed: false).cgPath
    points?.removeAll()
}
}


extension UIBezierPath {

static func interpolateHermiteFor(points: [CGPoint], closed: Bool = false) -> UIBezierPath {
    guard points.count >= 2 else {
        return UIBezierPath()
    }

    if points.count == 2 {
        let bezierPath = UIBezierPath()
        bezierPath.move(to: points[0])
        bezierPath.addLine(to: points[1])
        return bezierPath
    }

    let nCurves = closed ? points.count : points.count - 1

    let path = UIBezierPath()
    for i in 0..<nCurves {
        var curPt = points[i]
        var prevPt: CGPoint, nextPt: CGPoint, endPt: CGPoint
        if i == 0 {
            path.move(to: curPt)
        }

        var nexti = (i+1)%points.count
        var previ = (i-1 < 0 ? points.count-1 : i-1)

        prevPt = points[previ]
        nextPt = points[nexti]
        endPt = nextPt

        var mx: CGFloat
        var my: CGFloat
        if closed || i > 0 {
            mx  = (nextPt.x - curPt.x) * CGFloat(0.5)
            mx += (curPt.x - prevPt.x) * CGFloat(0.5)
            my  = (nextPt.y - curPt.y) * CGFloat(0.5)
            my += (curPt.y - prevPt.y) * CGFloat(0.5)
        }
        else {
            mx = (nextPt.x - curPt.x) * CGFloat(0.5)
            my = (nextPt.y - curPt.y) * CGFloat(0.5)
        }

        var ctrlPt1 = CGPoint.zero
        ctrlPt1.x = curPt.x + mx / CGFloat(3.0)
        ctrlPt1.y = curPt.y + my / CGFloat(3.0)

        curPt = points[nexti]

        nexti = (nexti + 1) % points.count
        previ = i;

        prevPt = points[previ]
        nextPt = points[nexti]

        if closed || i < nCurves-1 {
            mx  = (nextPt.x - curPt.x) * CGFloat(0.5)
            mx += (curPt.x - prevPt.x) * CGFloat(0.5)
            my  = (nextPt.y - curPt.y) * CGFloat(0.5)
            my += (curPt.y - prevPt.y) * CGFloat(0.5)
        }
        else {
            mx = (curPt.x - prevPt.x) * CGFloat(0.5)
            my = (curPt.y - prevPt.y) * CGFloat(0.5)
        }

        var ctrlPt2 = CGPoint.zero
        ctrlPt2.x = curPt.x - mx / CGFloat(3.0)
        ctrlPt2.y = curPt.y - my / CGFloat(3.0)

        path.addCurve(to: endPt, controlPoint1:ctrlPt1, controlPoint2:ctrlPt2)
    }

    if closed {
        path.close()
    }

    return path
}
}
/** To get mid point

- parameter firstPoint  
- parameter secondPont 

*/

CGPoint midPoint(CGPoint firstPoint, CGPoint secondPont)
{

    return CGPointMake((firstPoint.x + secondPont.x) * 0.5, (firstPoint.y + secondPont.y) * 0.5);

}
/** 
 touch began method
*/
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{

    UITouch *touch = [touches anyObject];

    previousPoint1 = [touch previousLocationInView:self];
    previousPoint2 = [touch previousLocationInView:self];
    currentPoint = [touch locationInView:self];

}

-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{

    UITouch *touch = [touches anyObject];

    previousPoint2 = previousPoint1;
    previousPoint1 = [touch previousLocationInView:self];
    currentPoint = [touch locationInView:self];


    // calculate mid point
    CGPoint mid1 = midPoint(previousPoint1, previousPoint2); 
    CGPoint mid2 = midPoint(currentPoint, previousPoint1);

    UIGraphicsBeginImageContext(self.imageView.frame.size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    [self.imageView.image drawInRect:CGRectMake(0, 0, self.imageView.frame.size.width, self.imageView.frame.size.height)];

    CGContextMoveToPoint(context, mid1.x, mid1.y);
    // Use QuadCurve is the key
    CGContextAddQuadCurveToPoint(context, previousPoint1.x, previousPoint1.y, mid2.x, mid2.y); 

    CGContextSetLineCap(context, kCGLineCapRound);
    CGContextSetLineWidth(context, 2.0);
    CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0);
    CGContextStrokePath(context);

    self.imageView.image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

}
import UIKit

class drawImageView: UIImageView {

    var previousPoint1 = CGPoint()

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let touch = touches.first else { return }
        previousPoint1 = touch.previousLocation(in: self)
    }

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let touch = touches.first else { return }

        let previousPoint2 = previousPoint1
        previousPoint1 = touch.previousLocation(in: self)
        let currentPoint = touch.location(in: self)


        // calculate mid point
        let mid1 = midPoint(p1: previousPoint1, p2: previousPoint2)
        let mid2 = midPoint(p1: currentPoint, p2: previousPoint1)

        UIGraphicsBeginImageContext(self.frame.size)
        guard let context = UIGraphicsGetCurrentContext() else { return }
        if let image = self.image {
            image.draw(in: CGRect(x: 0, y: 0, width: frame.size.width, height: frame.size.height))
        }

        context.move(to: mid1)
        context.addQuadCurve(to: mid2, control: previousPoint1)

        context.setLineCap(.round)
        context.setLineWidth(2.0)
        context.setStrokeColor(red: 1.0, green: 0, blue: 0, alpha: 1.0)
        context.strokePath()

        self.image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
    }

    func midPoint(firstPoint: CGPoint, secondPoint: CGPoint) -> CGPoint {
        return CGPoint(x: (firstPoint.x + secondPoint.x) / 2.0, y: (firstPoint.y + secondPoint.y) / 2.0)
    }
}