Ios 在Swift中连续绘制UIBezierPath期间消除滞后延迟

Ios 在Swift中连续绘制UIBezierPath期间消除滞后延迟,ios,swift,drawing,uibezierpath,Ios,Swift,Drawing,Uibezierpath,下面的代码通过覆盖接触来绘制线条,但是在连续不间断绘制的一段时间内开始出现滞后。手指在屏幕上移动的时间越长,滞后现象越严重。结果是,实际设备(CPU 98%+)上的CPU几乎达到最大值,绘图持续时间越长,生成的图像看起来就越不稳定 此外,当以极快的速度绘制时(尤其是在圆中),在路径和临时路径(或本地路径)之间绘制的路径存在差异。虽然它们是在不同的时间绘制的,但它们似乎同时出现在屏幕上,这在视觉上分散了看到两条路径快速绘制的注意力。内部路径(路径)似乎与外部路径(临时路径)有一段距离,在下图中以红

下面的代码通过覆盖接触来绘制线条,但是在连续不间断绘制的一段时间内开始出现滞后。手指在屏幕上移动的时间越长,滞后现象越严重。结果是,实际设备(CPU 98%+)上的CPU几乎达到最大值,绘图持续时间越长,生成的图像看起来就越不稳定

此外,当以极快的速度绘制时(尤其是在圆中),在
路径
临时路径
(或
本地路径
)之间绘制的路径存在差异。虽然它们是在不同的时间绘制的,但它们似乎同时出现在屏幕上,这在视觉上分散了看到两条路径快速绘制的注意力。内部路径(
路径
)似乎与外部路径(
临时路径
)有一段距离,在下图中以红色突出显示

1-如何消除连续拉伸期间的滞后延迟

2-如何消除绘制路径中的差异

3-如何更改
路径
临时路径
的alpha/不透明度

class swiftView:UIView{
var strokeColor=UIColor.blueColor()
变量线宽:CGFloat=5
var快照映像:UIImage?
私有变量路径:UIBezierPath?
私有var临时路径:UIBezierPath?
私有变量点=[CGPoint]()
var对应点:Int?
必需的初始化?(编码器aDecoder:NSCoder){
super.init(编码者:aDecoder)
}
重写func drawRect(rect:CGRect){
自动释放池{
快照图像?.drawInRect(rect)
strokeColor.setStroke()
路径?.stroke()
时间路径?.stroke()
}
}
覆盖功能触摸开始(触摸:设置,withEvent事件:UIEvent?){
让触摸:AnyObject?=首先触摸
点=[触摸!.locationInView(自)]
对手得分=0
}
覆盖功能触摸移动(触摸:设置,带事件:UIEvent?){
让触摸:AnyObject?=首先触摸
让点=触摸!.locationInView(自)
点。追加(点)
设pointCount=points.count
对手得分=对手得分!+1
如果pointCount==2{
临时路径=createPathStartingAtPoint(点[0])
临时路径?.addLineToPoint(点[1])
setNeedsDisplay()
}如果pointCount==3,则为else{
临时路径=createPathStartingAtPoint(点[0])
时间路径?.addQuadCurveToPoint(点[2],控制点:点[1])
setNeedsDisplay()
}如果pointCount==4,则为else{
临时路径=createPathStartingAtPoint(点[0])
temporaryPath?.addCurveToPoint(点[3],控制点1:点[1],控制点2:点[2])
//setNeedsDisplay()
如果对方得分低于50分{
self.setNeedsDisplay()
}否则{
时间路径=零
self.constructionIncrementalImage()
路径=零
self.setNeedsDisplay()
对手得分=0
}
}如果pointCount==5,则为else{
点[3]=CGPointMake((点[2].x+点[4].x)/2.0,(点[2].y+点[4].y)/2.0)
//创建一个四次bezier,直到第4点
如果分数[4]!=分数[3]{
设长度=hypot(点[4]。x-点[3]。x,点[4]。y-点[3]。y)/2.0
设角度=atan2(点[3]。y-点[2]。y,点[4]。x-点[3]。x)
设controlPoint=CGPoint(x:points[3]。x+cos(angle)*长度,y:points[3]。y+sin(angle)*长度)
临时路径=createPathStartingAtPoint(点[3])
临时路径?.addQuadCurveToPoint(点[4],控制点:控制点)
}否则{
时间路径=零
}
如果路径==nil{
路径=createPathStartingAtPoint(点[0])
}
路径?.addCurveToPoint(点[3],控制点1:点[1],控制点2:点[2])
self.setNeedsDisplay()
点数=[点数[3],点数[4]]
}
}
覆盖func touchesEnded(触摸:设置,withEvent事件:UIEvent?){
self.constructionIncrementalImage()
路径=零
self.setNeedsDisplay()
对手得分=0
}
覆盖功能触摸已取消(触摸:设置?,带事件:UIEvent?){
self.touchesend(touchs!,with event:event)
}
私有函数createPathStartingAtPoint(点:CGPoint)->UIBezierPath{
让localPath=UIBezierPath()
localPath.moveToPoint(点)
localPath.lineWidth=线宽
localPath.lineCapStyle=.Round
localPath.lineJoinStyle=.Round
返回本地路径
}
private func constructionIncrementalImage(){
UIGraphicsBeginImageContextWithOptions(self.bounds.size,false,0.0)
strokeColor.setStroke()
快照图像?.drawAtPoint(CGPointZero)
路径?.stroke()
时间路径?.stroke()
snapshotImage=UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsSendImageContext()
}
}
您问:

  • 如何消除连续拉伸期间的滞后延迟
  • 正如您正确猜测的,是的,通过限制路径的长度,执行快照并重置路径可以解决这一问题

    我知道你知道这一点,但为了其他读者的利益,在iOS 9中你也可以使用预测触摸。在这个特定的算法中(其中(a)你只是简单地添加到一条路径,但是(b)每四个点在下一个点的基础上进行调整,以确保两条三次bezier曲线连接处没有间断),这有点棘手,但可以做到

  • 如何消除绘制路径中的差异
  • 这是因为快照包含临时路径。但这条临时路径的全部目的是,随着更多点的到来,它将被丢弃。因此,不应将其包含在创建的快照中

    因此,我建议在snapshot函数中添加一个参数,该参数指示是否应该
    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()
    }
    
    }
    
    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()
        }
    }
    
    override func drawRect(rect: CGRect) {
        snapshotImage?.drawInRect(rect)
    
        strokeColor.setStroke()
        path?.stroke()
    
        strokeColor.colorWithAlphaComponent(0.5).setStroke()
        temporaryPath?.stroke()
    }