Search code examples
swiftuiimageviewdrawsmoothing

How to draw smooth lines inside an UIImageView with Swift?


The below code draws straight lines between points onto a UIImageView. Drawings render fast and look good but could be much better, so I want to modify the code to create smooth curved lines. I’ve investigated a number of examples in Swift over the past six weeks with zero success.

What sections of the below code must I modify (and how) to achieve this please? I’ve been told calling the below snippet instead of CGContextAddLineToPoint() (at // 2 below) should do the trick, but I’m not clear on how to call it and how it relates to the current code entirely.

SNIPPET:

func drawQuadLineWithPoints(firstPoint: CGPoint, secondPoint: CGPoint, thirdPoint: CGPoint, inContext: CGContext) {
    CGContextMoveToPoint(context, 0.5 * (firstPoint.x + secondPoint.x), 0.5 * (firstPoint.y + secondPoint.y));
    CGContextAddQuadCurveToPoint(context, secondPoint.x, secondPoint.y, 0.5 * (secondPoint.x + thirdPoint.x), 0.5 * (secondPoint.y + thirdPoint.y));
}

CODE:

  override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
    swiped = false
    if let touch = touches.anyObject() as? UITouch {
      lastPoint = touch.locationInView(self.view)
    }
  }

  func drawLineFrom(fromPoint: CGPoint, toPoint: CGPoint) {

    // 1
    UIGraphicsBeginImageContext(view.frame.size)
    let context = UIGraphicsGetCurrentContext()
    tempImageView.image?.drawInRect(CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height))

    // 2
    CGContextMoveToPoint(context, fromPoint.x, fromPoint.y)
    CGContextAddLineToPoint(context, toPoint.x, toPoint.y)

    // 3
    CGContextSetLineCap(context, kCGLineCapRound)
    CGContextSetLineWidth(context, brushWidth)
    CGContextSetRGBStrokeColor(context, red, green, blue, 1.0)
    CGContextSetBlendMode(context, kCGBlendModeNormal)

    // 4
    CGContextStrokePath(context)

    // 5
    tempImageView.image = UIGraphicsGetImageFromCurrentImageContext()
    tempImageView.alpha = opacity
    UIGraphicsEndImageContext()

  }

  override func touchesMoved(touches: NSSet, withEvent event: UIEvent) {
    // 6
    swiped = true
    if let touch = touches.anyObject() as? UITouch {
      let currentPoint = touch.locationInView(view)
      drawLineFrom(lastPoint, toPoint: currentPoint)

      // 7
      lastPoint = currentPoint
    }
  }

  override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {

    if !swiped {
      // draw a single point
      drawLineFrom(lastPoint, toPoint: lastPoint)
    }

    // Merge tempImageView into mainImageView
    UIGraphicsBeginImageContext(mainImageView.frame.size)
    mainImageView.image?.drawInRect(CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height), blendMode: kCGBlendModeNormal, alpha: 1.0)
    tempImageView.image?.drawInRect(CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height), blendMode: kCGBlendModeNormal, alpha: opacity)
    mainImageView.image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    tempImageView.image = nil
  }

Solution

  • Look at the answer for THIS

    This is a rough translation to Swift I used:

    override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
    
    
        if let touch = touches.first as? UITouch{
    
            prevPoint1 = touch.previousLocationInView(self.view)
    
            prevPoint2 = touch.previousLocationInView(self.view)
    
            lastPoint = touch.locationInView(self.view)
    
        }
    
    }
    
    
    
    override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
    
    
        if let touch = touches.first as? UITouch{
    
            let currentPoint = touch.locationInView(view)
    
    
    
            prevPoint2 = prevPoint1
    
            prevPoint1 = touch.previousLocationInView(self.view)
    
    
    
    
    
            UIGraphicsBeginImageContext(view.frame.size)
    
            let context = UIGraphicsGetCurrentContext()
    
            TempImage.image?.drawInRect(CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height))
    
    
    
            var mid1 = CGPointMake((prevPoint1.x + prevPoint2.x)*0.5, (prevPoint1.y + prevPoint2.y)*0.5)
    
            var mid2 = CGPointMake((currentPoint.x + prevPoint1.x)*0.5, (currentPoint.y + prevPoint1.y)*0.5)
    
    
    
            CGContextMoveToPoint(context, mid1.x, mid1.y)
    
            CGContextAddQuadCurveToPoint(context, prevPoint1.x, prevPoint1.y, mid2.x, mid2.y)
    
            //CGContextAddLineToPoint(context, toPoint.x, toPoint.y)
    
    
    
            CGContextSetLineCap(context, kCGLineCapRound)
    
            CGContextSetLineWidth(context, brushWidth)
    
            CGContextSetRGBStrokeColor(context, red, green,blue, 1.0)
    
            CGContextSetBlendMode(context, kCGBlendModeNormal)
    
    
    
            CGContextStrokePath(context)
    
    
    
            TempImage.image = UIGraphicsGetImageFromCurrentImageContext()
    
            TempImage.alpha = opacity
    
            UIGraphicsEndImageContext()
    
    
    
            lastPoint = currentPoint
    
        }
    
    }
    
    
    
    override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
    
    
    
        UIGraphicsBeginImageContext(MainImage.frame.size)
    
        MainImage.image?.drawInRect(CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height), blendMode: kCGBlendModeNormal, alpha: 1.0)
    
        TempImage.image?.drawInRect(CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height), blendMode: kCGBlendModeNormal, alpha: opacity)
    
        MainImage.image = UIGraphicsGetImageFromCurrentImageContext()
    
        UIGraphicsEndImageContext()
    
    
    
        TempImage.image = nil
    
    }