在Swift中绘制UIBezierPath平滑线时消除滞后延迟 [英] Removing lagging latency in drawing UIBezierPath smooth lines in Swift

查看:121
本文介绍了在Swift中绘制UIBezierPath平滑线时消除滞后延迟的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

下面的代码通过覆盖触摸来绘制平滑的曲线,但是有明显的滞后或延迟。该代码使用 addCurveToPoint 并在每4个触摸点后调用 setNeedsDisplay ,这会导致绘图出现跳跃状态,因为图形不能保持用手指动作。要消除滞后或感知延迟,可以使用 addQuadCurveToPoint addLineToPoint临时填充触点1,2,3(通向触点4)

The code below draws smooth curved lines by overriding touches, but there is noticeable lagging or latency. The code uses addCurveToPoint and calls setNeedsDisplay after every 4 touch points which causes a jumpy appearance as the drawing doesn't keep up with finger movements. To remove the lagging or perceived latency, touch points 1, 2, 3 (leading up to touch point 4) could be temporarily filled with addQuadCurveToPoint and addLineToPoint.


  1. 如何在代码中实现这一点,以通过使用临时线来消除感知到的滞后显示最终曲线前的QuadCurved线?

  1. How can this actually be achieved in code to remove perceived lagging by using a temporary Line and QuadCurved line before displaying a final Curved line?

如果以下类附加到一个 UIView (例如viewOne或 self ),如何将绘图副本复制到课堂外的另一个 UIView (例如viewTwo) touchesEnded 之后?

If the below class is attached to one UIView (e.g. viewOne or self), how do I make a copy of the drawing to another UIView outside the class (e.g. viewTwo) after touchesEnded?

 //  ViewController.swift

import UIKit

class drawSmoothCurvedLinesWithLagging: UIView {

    let path=UIBezierPath()
    var incrementalImage:UIImage?

    var points = [CGPoint?](count: 5, repeatedValue: nil)

    var counter:Int?

    var strokeColor:UIColor?

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    override func drawRect(rect: CGRect) {
        autoreleasepool {
            incrementalImage?.drawInRect(rect)
            strokeColor = UIColor.blueColor()
            strokeColor?.setStroke()
            path.lineWidth = 20
            path.lineCapStyle = CGLineCap.Round
            path.stroke()
        }
    }

    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
        counter = 0

        let touch: AnyObject? = touches.first
        points[0] = touch!.locationInView(self)
    }

    override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
        let touch: AnyObject? = touches.first
        let point = touch!.locationInView(self)

        counter = counter! + 1
        points[counter!] = point


        if counter == 2{
            //use path.addLineToPoint ?
            //use self.setNeedsDisplay() ?
        }

        if counter == 3{
            //use path.addQuadCurveToPoint ?
            //use self.setNeedsDisplay() ?
        }

        if counter == 4{
            points[3]! = CGPointMake((points[2]!.x + points[4]!.x)/2.0, (points[2]!.y + points[4]!.y)/2.0)
            path.moveToPoint(points[0]!)
            path.addCurveToPoint(points[3]!, controlPoint1: points[1]!, controlPoint2: points[2]!)

            self.setNeedsDisplay()

            points[0]! = points[3]!
            points[1]! = points[4]!
            counter = 1
        }
    }

    override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
        self.drawBitmap()
        self.setNeedsDisplay()
        path.removeAllPoints()
        counter = 0
    }

    override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) {
        self.touchesEnded(touches!, withEvent: event)
    }

    func drawBitmap(){
        UIGraphicsBeginImageContextWithOptions(self.bounds.size, true, 0.0)
        strokeColor?.setStroke()
        if((incrementalImage) == nil){
            let rectPath:UIBezierPath = UIBezierPath(rect: self.bounds)
            UIColor.whiteColor().setFill()
            rectPath.fill()
        }

        incrementalImage?.drawAtPoint(CGPointZero)
        path.stroke()
        incrementalImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
    }

}

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


}



推荐答案


  1. 是的,每隔几个点添加一条曲线就会给它带来口吃滞后。所以,是的,您可以通过在 points [1] 中添加一行来减少这种影响,将四条曲线添加到 points [2] 并将一条三次曲线添加到 points [3]

  1. Yes, adding a curve every few points will give it a stuttering lag. So, yes, you can reduce this affect by adding a line to points[1], adding a quad curve to points[2] and adding a cubic curve to points[3].

如你所说,请确保但是,将其添加到单独的路径中。所以,在Swift 3/4中:

As you said, make sure to add this to a separate path, though. So, in Swift 3/4:

class SmoothCurvedLinesView: UIView {
    var strokeColor = UIColor.blue
    var lineWidth: CGFloat = 20
    var snapshotImage: UIImage?

    private var path: UIBezierPath?
    private var temporaryPath: UIBezierPath?
    private var points = [CGPoint]()

    override func draw(_ rect: CGRect) {
        snapshotImage?.draw(in: rect)

        strokeColor.setStroke()

        path?.stroke()
        temporaryPath?.stroke()
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        if let touch = touches.first {
            points = [touch.location(in: self)]
        }
    }

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

        points.append(point)

        updatePaths()

        setNeedsDisplay()
    }

    private func updatePaths() {
        // update main path

        while points.count > 4 {
            points[3] = CGPoint(x: (points[2].x + points[4].x)/2.0, y: (points[2].y + points[4].y)/2.0)

            if path == nil {
                path = createPathStarting(at: points[0])
            }

            path?.addCurve(to: points[3], controlPoint1: points[1], controlPoint2: points[2])

            points.removeFirst(3)

            temporaryPath = nil
        }

        // build temporary path up to last touch point

        if points.count == 2 {
            temporaryPath = createPathStarting(at: points[0])
            temporaryPath?.addLine(to: points[1])
        } else if points.count == 3 {
            temporaryPath = createPathStarting(at: points[0])
            temporaryPath?.addQuadCurve(to: points[2], controlPoint: points[1])
        } else if points.count == 4 {
            temporaryPath = createPathStarting(at: points[0])
            temporaryPath?.addCurve(to: points[3], controlPoint1: points[1], controlPoint2: points[2])
        }
    }

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

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

    private func finishPath() {
        constructIncrementalImage()
        path = nil
        setNeedsDisplay()
    }

    private func createPathStarting(at point: CGPoint) -> UIBezierPath {
        let localPath = UIBezierPath()

        localPath.move(to: point)

        localPath.lineWidth = lineWidth
        localPath.lineCapStyle = .round
        localPath.lineJoinStyle = .round

        return localPath
    }

    private func constructIncrementalImage() {
        UIGraphicsBeginImageContextWithOptions(bounds.size, false, 0.0)
        strokeColor.setStroke()
        snapshotImage?.draw(at: .zero)
        path?.stroke()
        temporaryPath?.stroke()
        snapshotImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
    }
}



<你甚至可以将它与iOS 9的预测接触结合起来(正如我所描述的那样在我的另一个答案中),这可以进一步减少滞后。

You could even marry this with iOS 9 predictive touches (as I described in my other answer), which could reduce lag even further.

要获取此结果图像并在其他地方使用它,您只需获取 incrementalImage (我将其重命名为 snapshotImage ,上面),并将其放入另一个视图的图像视图中。

To take this resulting image and use it elsewhere, you can just grab the incrementalImage (which I renamed to snapshotImage, above), and drop it into an image view of the other view.

对于Swift 2的再现,请参阅之前的修订此答案

For Swift 2 rendition, see previous revision of this answer.

这篇关于在Swift中绘制UIBezierPath平滑线时消除滞后延迟的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆