Search code examples
iosswiftuikitcore-graphics

Drawing perfect rounding lines, Swift


I'm working on a drawing app prototype and what I want to achieve is perfect rounding lines. I've implemented drawing via curves but still, my lines look a little bit pixelated.

Here is my result:

enter image description here

and here is what I'm trying to achieve:

enter image description here

    class ViewController: UIViewController {
        private var currentPoint: CGPoint?
        private var previousPoint1: CGPoint?
        private var previousPoint2: CGPoint?
        private var lineColor = UIColor.black
        private var lineWidth = CGFloat(10)
        private var lineAlpha = CGFloat(1)
        @IBOutlet private weak var imageView: UIImageView!
    
        override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
            guard let touch = touches.first else { return }
            
            previousPoint1 = touch.previousLocation(in: self.view)
            currentPoint = touch.location(in: self.view)
        }
    
        override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
            guard let touch = touches.first else { return }
            
            previousPoint2 = previousPoint1
            previousPoint1 = touch.previousLocation(in: self.view)
            currentPoint = touch.location(in: self.view)
            
            let mid1 = middlePoint(previousPoint1!, previousPoint2: previousPoint2!)
            let mid2 = middlePoint(currentPoint!, previousPoint2: previousPoint1!)
            
            draw(move: CGPoint(x: mid1.x, y: mid1.y), to: CGPoint(x: mid2.x, y: mid2.y), control: CGPoint(x: previousPoint1!.x, y: previousPoint1!.y))
        }
    
        public override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
            touchesMoved(touches, with: event)
        }
    
        private func middlePoint(_ previousPoint1: CGPoint, previousPoint2: CGPoint) -> CGPoint {
            return CGPoint(x: (previousPoint1.x + previousPoint2.x) * 0.5, y: (previousPoint1.y + previousPoint2.y) * 0.5)
        }
    
        func draw(move: CGPoint, to: CGPoint, control: CGPoint) {
            UIGraphicsBeginImageContext(view.frame.size)
            guard let ctx = UIGraphicsGetCurrentContext() else { return }
            imageView.image?.draw(in: view.bounds)
            ctx.move(to: move)
            ctx.addQuadCurve(to: to, control: control)
            ctx.setLineCap(.round)
            ctx.setLineJoin(.round)
            ctx.setLineWidth(lineWidth)
            ctx.setStrokeColor(lineColor.cgColor)
            ctx.setBlendMode(.normal)
            ctx.setAlpha(lineAlpha)
            ctx.strokePath()
            let finalImage = UIGraphicsGetImageFromCurrentImageContext()
            imageView.image = finalImage
            UIGraphicsEndImageContext()
        }
    }

P.S. I want to draw via context, not using drawRect


Solution

  • The problem here is that the scale of imageContext you are creating is not correct. Rather than creating your context with UIGraphicsBeginImageContext try to create it with UIGraphicsBeginImageContextWithOptions and give scale value as 0. When you specify a scale value of 0, the scale factor is set to the scale factor of the device’s main screen. Updated code snippet would be like the one follows.

    func draw(move: CGPoint, to: CGPoint, control: CGPoint) {
            UIGraphicsBeginImageContextWithOptions(view.frame.size, false, 0)
            guard let ctx = UIGraphicsGetCurrentContext() else { return }
            imageView.image?.draw(in: view.bounds)
            ctx.move(to: move)
            ctx.addQuadCurve(to: to, control: control)
            ctx.setLineCap(.round)
            ctx.setLineJoin(.round)
            ctx.setLineWidth(lineWidth)
            ctx.setStrokeColor(lineColor.cgColor)
            ctx.setBlendMode(.normal)
            ctx.setAlpha(lineAlpha)
            ctx.strokePath()
            let finalImage = UIGraphicsGetImageFromCurrentImageContext()
            imageView.image = finalImage
            UIGraphicsEndImageContext()
        }