Drawing curved lines without lagging?

2019-06-08 12:47发布

I have a class below that when attached to a view draws curved lines when the user touches their device. The problem is that the lines drawn seem to lag behind from the position of the finger on the screen. The lagging is enough to be noticeable and mildly annoying as new sections of the line display a small distance away from the finger touching the screen.

The code uses the addCurveToPoint curve method. (The alternative addQuadCurveToPoint curve method appears to be less superior in terms of a quality curved line but does display on screen faster.)

I suspect that this issue relates to when setNeedsDisplay is called once the counter == 4. It appears the code waits until 4 new touch points are received while drawing before a curved line is drawn. Ideally a curved line is drawn at every single touch point (i.e. counter == 1), eliminating the lagging. (Changing Counter == 1 doesn't seem to work.)

I'm lost and don't know how to update the code to improve it further to remove that short lag but retain the curved lines. What needs to change in the below code to remove that short lag?

// Swift 2 code below tested using Xcode 7.0.1.

class drawView: UIView {

var path:UIBezierPath?
var incrementalImage:UIImage?

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

var infoView:UIView = UIView()
var strokeColor:UIColor?


required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    self.multipleTouchEnabled = false
    self.backgroundColor = UIColor.whiteColor()
    path = UIBezierPath()
    path?.lineWidth = 20.0
    strokeColor = UIColor.darkGrayColor()
    path?.lineCapStyle = CGLineCap.Round
}

override init(frame: CGRect) {
    super.init(frame: frame)
    self.multipleTouchEnabled = false
    path = UIBezierPath()
    path?.lineWidth = 20.0
}


override func drawRect(rect: CGRect) {
    incrementalImage?.drawInRect(rect)
    strokeColor?.setStroke()
    path?.stroke()
}

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
    counter = 0
    let touch: AnyObject? = touches.first
    points[0] = touch!.locationInView(self)
    infoView.removeFromSuperview()
}

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

1条回答
戒情不戒烟
2楼-- · 2019-06-08 13:22

To start, I believe that you are doing it wrong. This usally works well if you want to draw a few lines not nesscarly by the users input but for circles, squiggly lines, simple things.

when using:

self.setNeedsDisplay()

You are redrawing ALL the lines EVERYTIME! this is tough CPU and that's why you have a lag. Image the user draws a few hundred lines then into a thousand and everytime he/she touches the screen it will redraw ALL of those lines.

OK. So, What I recommend doing is have 2 UIImageViews: 1) mainImageView - which will hold the overall drawing. 2) tempImageView - which the user will use to draw.

When the user touches/draws on "tempImageView" it draws until they let go of the screen then merge "tempImageView" to "mainImageView"

Here is a tutorial on:

http://www.raywenderlich.com/87899/make-simple-drawing-app-uikit-swift

查看更多
登录 后发表回答