Simple drawing view swift 3.0

I would just like some sample code for a view that you can draw on in the current version of swift. I have looked through a few tutorials and the only one I can find that works looks like the code below. I dont want to use this implementation because it is redrawing the entire image from scratch each time I move my finger, meaning it gets very slow as the image I am drawing becomes more complex. Could someone point me in the direction or give me an idea of how it should be done? Thank you!


import UIKit
class DrawingView : UIView {
   
    private class Line {
        var startPont : CGPoint
        var endPoint : CGPoint
        init(start : CGPoint, end : CGPoint) {
            startPont = start
            endPoint = end
        }
    }
   
    var lastPoint : CGPoint!
    private var lines = [Line]()
    var tempImage : UIImageView! = UIImageView()
   
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        for touch in touches {
            lastPoint = touch.location(in: self)
            lines.append(Line(start: lastPoint, end: lastPoint))
            self.setNeedsDisplay()
        }
    }
   
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        for touch in touches {
            let newPoint = touch.location(in: self)
            lines.append(Line(start: lastPoint, end: newPoint))
            lastPoint = newPoint
            self.setNeedsDisplay()
        }
    }
   
    override func draw(_ rect: CGRect) {
        let context = UIGraphicsGetCurrentContext()
        for line in lines {
            context?.beginPath()
            context?.move(to: line.startPont)
            context?.addLine(to: line.endPoint)
            context?.setLineCap(CGLineCap.round)
            context?.setFillColor(UIColor.black.cgColor)
            context?.setStrokeColor(UIColor.black.cgColor)
            context?.setLineWidth(8.0)
            context?.strokePath()
        }
    }
   
    func clear() {
        lines = [Line]()
        self.setNeedsDisplay()
    }
}

Have you observed some sort of significant slow downs using the code?


(The response may not seem to be quick enough, but it may happen even if your `lines` is very small. It's the nature of `UIView`.)


Your code shown is a typical implementation of drawing view.


What part of this code do you think is inefficient?

Do you know that you need to redraw whole region specified with `rect` when implementing `draw(_:)` of a `UIView`?

Thank you for responding, so I have noticed some signifigant slow downs. As I draw more things, there is more lag in the drawing and my new paths essentially devolve into very noticiable straight lines (I understand that everything I am drawing is a straight line but it isnt noticeable sine it updates so quickly when there are few lines).


I think it is inefficient because every time I move my finger on the screen a new line is added to my array and every line in my array is redrawn, so as I add more lines it takes signifgantly longer to draw the lines. What I am looking for is a way to keep what I have on screen and then just draw a new line on top of it each time touchesMoved method is called and not have to store every line in an array.


I am coming from javascript in which you would just draw something on the canvas and it would stay even when I drew something else on it. Quartz seems to erase everything each time I draw on it.

I have noticed some signifigant slow downs.

As far as I tested, no such slow downs can be observed, just that it's always slow. I have tested with just a few hundreds of lines, you may have tested with thousands or more.


every line in my array is redrawn

As I already wrote, it's a requirement implementing `draw(_:)` in `UIView`.


What I am looking for is a way to keep what I have on screen

you would just draw something on the canvas and it would stay even when I drew something else on it.

Then you need to find other things than overriding `draw(_:)` of `UIView`. The drawing system of `UIView` is completely different than canvas of JavaScript.


Quartz seems to erase everything each time I draw on it.

Right. That makes the requirement I wrote above.


One way is to create a canvas-like offline bitmap, and you draw on it. And in `draw(_:)`, draw the offline bitmap onto the screen buffer. It's the similar way usual browsers showing the content of canvas.

So this is what I have come up with so far. It now has no issues with slowing down like the other method, but it strangely blurs and pushes the lines to near the top of the view. What should I change?


override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        if let touch = touches.first {
            lastPoint = touch.location(in: self)
        }
    }
   
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        if let touch = touches.first {
            let currentPoint = touch.location(in: self)
            drawLine(from: lastPoint, to: currentPoint)
            lastPoint = currentPoint
        }
    }
   
    func drawLine(from lastPoint : CGPoint, to newPoint : CGPoint) {
        UIGraphicsBeginImageContext(self.bounds.size)
        self.image?.draw(in: self.bounds)
        let context = UIGraphicsGetCurrentContext()
       
        context?.move(to: lastPoint)
        context?.addLine(to: newPoint)
       
        context?.setBlendMode(CGBlendMode.normal)
        context?.setLineCap(CGLineCap.round)
        context?.setLineWidth(5)
        context?.setStrokeColor(UIColor.black.cgColor)
       
        context?.strokePath()
       
        self.image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
    }
   
    func clear() {
        self.image = nil
    }

Why are you hiding other parts of your code? At least some of the reasons causing the issue would be hiding inside the hidden code.

I did not post all of it because I am trying to reduce what you have to look at, but here is the whole file.

import UIKit
class DrawingView : UIImageView {
  
    var lastPoint : CGPoint!  


    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        if let touch = touches.first {
            lastPoint = touch.location(in: self)
        }
    }
  
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        if let touch = touches.first {
            let currentPoint = touch.location(in: self)
            drawLine(from: lastPoint, to: currentPoint)
            lastPoint = currentPoint
        }
    }
  
    func drawLine(from lastPoint : CGPoint, to newPoint : CGPoint) {
        UIGraphicsBeginImageContext(self.bounds.size)
        self.image?.draw(in: self.bounds)
        let context = UIGraphicsGetCurrentContext()
      
        context?.move(to: lastPoint)
        context?.addLine(to: newPoint)
      
        context?.setBlendMode(CGBlendMode.normal)
        context?.setLineCap(CGLineCap.round)
        context?.setLineWidth(5)
        context?.setStrokeColor(UIColor.black.cgColor)
      
        context?.strokePath()
      
        self.image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
    }
  
    func clear() {
        self.image = nil
    }
}


Edit: I ran the code in a separate project as a view that covers the whole screen and it worked fine. In my current project, I was placing an instance of this view in the bottom half of my screen via the storyboard, so I think it has something to do with that. Any ideas?

Seems you have your `DrawingView` as a subclass of `UIImageView`, it's a very important info which is not referred to in your older post.


And placing an instance of this view in the bottom half of my screen via the storyboard also maybe affecting.


But, sorry I cannot have enough time to test your code now. When I can take some time before you or any other solves this issue, I'll work on it.

Have you made any progress?


As far as I tested, with putting your new `DrawingView` as a subview of other view via storyboard, I cannot observe the issue you describe.

You may need to find what is special in your current project.

Isn't the CGRect passed into draw the cropping rect for where drawing needs to happen? It seems like you should be drawing into an offline buffer and then just blitting from that drawing into the CGRect area.

Simple drawing view swift 3.0
 
 
Q