how to freely draw / doodle a line on an Image in swift? - ios

i have searched the solution for this case, but i didn't find it on the internet. i want to draw/doodle a line like snapchat or whatsapp status that allow user to draw freely. I found a similar tutorial but it just drew on the UIView in this tutorial : https://www.youtube.com/watch?v=gbFHqHHApC4&t=7s
i want to allow user to freely draw on the image they pick from image gallery/camera, and then save the result back as the UIImage
the source code of the doodle on the UIView (the first picture) is below
first, make the custom view
class DrawView : ShadowView {
var isDrawing = false
var lastPoint : CGPoint!
var strokeColor : CGColor! = UIColor.black.cgColor
var strokes = [Stroke]()
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard !isDrawing else { return }
isDrawing = true
guard let touch = touches.first else {return}
let currentPoint = touch.location(in: self)
lastPoint = currentPoint
print(currentPoint)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard isDrawing else { return}
guard let touch = touches.first else {return}
let currentPoint = touch.location(in: self)
let stroke = Stroke(startPoint: lastPoint, endPoint: currentPoint, strokeColor: strokeColor)
strokes.append(stroke)
lastPoint = currentPoint
setNeedsDisplay()
print(currentPoint)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
guard isDrawing else { return}
isDrawing = false
guard let touch = touches.first else {return}
let currentPoint = touch.location(in: self)
let stroke = Stroke(startPoint: lastPoint, endPoint: currentPoint, strokeColor: strokeColor)
strokes.append(stroke)
setNeedsDisplay()
lastPoint = nil
print(currentPoint)
}
override func draw(_ rect: CGRect) {
let context = UIGraphicsGetCurrentContext()
context?.setLineWidth(5)
context?.setLineCap(.round)
for stroke in strokes {
context?.beginPath()
context?.move(to: stroke.startPoint)
context?.addLine(to: stroke.endPoint)
context?.setStrokeColor(stroke.strokeColor)
context?.strokePath()
}
}
func erase() {
strokes = []
strokeColor = UIColor.black.cgColor
setNeedsDisplay() // ditampilkan ke layar
}
}
The struct of the stoke is like this one :
struct Stroke {
let startPoint : CGPoint
let endPoint : CGPoint
let strokeColor : CGColor
}
after that, assign the custom view to ViewController :
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var drawView: DrawView!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func yellowDidPressed(_ sender: Any) {
drawView.strokeColor = UIColor.yellow.cgColor
}
#IBAction func redDidPressed(_ sender: Any) {
drawView.strokeColor = UIColor.red.cgColor
}
#IBAction func blueDidPressed(_ sender: Any) {
drawView.strokeColor = UIColor.blue.cgColor
}
#IBAction func erasedDidPressed(_ sender: Any) {
drawView.erase()
}
}
so how if i want to apply that custom view on the UIImageView and save the result as the new UIImage? I am new in programming actually, really needyour help. Thanks :)

Related

How to erase Core Graphics Context drawing in Swift?

I have used core graphics in a view to drawing lines. Now, I need to erase the lines that I daw using an erase button. I don't know what should I do to get a clear view with no lines.
Here is the code I used for drawing lines.
struct Line {
var points: [CGPoint]
var strokeColor: UIColor
var strokeWidth: CGFloat
}
class CanvasView: UIView {
private var lines: [Line] = []
private var strokeWidth: CGFloat = 8.0
private var strokeColor: UIColor = .white
override func draw(_ rect: CGRect) {
super.draw(rect)
guard let context = UIGraphicsGetCurrentContext() else { return }
lines.forEach { line in
context.setStrokeColor(line.strokeColor.cgColor)
context.setLineWidth(line.strokeWidth)
context.setLineCap(.round)
for (index, point) in line.points.enumerated() {
if index == 0 {
context.move(to: point)
} else {
context.addLine(to: point)
}
}
context.strokePath()
}
}
override func touchesBegan(_ touches: Set<UITouch>, with: event: UIEvent?) {
guard let point = touches.first.location(in: self) else { return }
let newLine = Line(points: [point], strokeColor: strokeColor, strokeWidth: strokeWidth)
lines.append(newLine)
setNeedsDisplay()
}
override func touchesMoved(_ touches: Set<UITouch>, with: event: UIEvent?) {
guard let point = touches.first.location(in: self) else { return }
guard var lastLine = lines.popLast() else { return }
lastLine.points.append(point)
lines.append(lastLine)
setNeedsDisplay()
}
}
I have connected this CanvasView: UIView class to my own view to use Core Graphics Context drawing.
I need a way to erase what I draw in my UIView using a button click.
Your drawing is depends on your list of Line so the thing you just need to do is make func where you clear all your lines then redraw. So at this time, when you call draw(_ rect:) again check if there is no value in your lines then clear view.
class CanvasView: UIView {
private var lines: [Line] = []
private var strokeWidth: CGFloat = 8.0
private var strokeColor: UIColor = .white
func clearView() {
self.lines = []
setNeedsDisplay()
}
override func draw(_ rect: CGRect) {
super.draw(rect)
guard let context = UIGraphicsGetCurrentContext() else { return }
if self.lines.count == 0 {
// context clear here
context.clear(self.bounds)
}
lines.forEach { line in
context.setStrokeColor(line.strokeColor.cgColor)
context.setLineWidth(line.strokeWidth)
context.setLineCap(.round)
for (index, point) in line.points.enumerated() {
if index == 0 {
context.move(to: point)
} else {
context.addLine(to: point)
}
}
context.strokePath()
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let point = touches.first?.location(in: self) else { return }
let newLine = Line(points: [point], strokeColor: strokeColor, strokeWidth: strokeWidth)
lines.append(newLine)
setNeedsDisplay()
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let point = touches.first?.location(in: self) else { return }
guard var lastLine = lines.popLast() else { return }
lastLine.points.append(point)
lines.append(lastLine)
setNeedsDisplay()
}
}
And in your ViewController just simply call func canvasView.clearView()

How to draw rectangles on a image in swift

Hello I'm trying to draw some rectangles on a UIImageView but when I add the background image the rectangle drawing is not working anymore:
This is the code:
import UIKit
class DrawView: UIView {
var startPos: CGPoint?
var endPos: CGPoint?
override func draw(_ rect: CGRect) {
guard let context = UIGraphicsGetCurrentContext() else {
return
}
if startPos != nil && endPos != nil {
print("both there")
drawRectangle(context: context, startPoint: startPos!, endPoint: endPos!, isFilled: false)
}
}
public func drawRectangle(context: CGContext, startPoint: CGPoint, endPoint: CGPoint, isFilled: Bool) {
let xx = startPoint
let xy = CGPoint(x:endPoint.x, y:startPoint.y)
let yx = CGPoint(x:startPoint.x, y:endPoint.y)
let yy = endPoint
print("draw rectangle")
context.move(to: xx)
context.addLine(to: xx)
context.addLine(to: xy)
context.addLine(to: yy)
context.addLine(to: yx)
context.addLine(to: xx)
context.setLineCap(.square)
context.setLineWidth(8)
if isFilled {
context.setFillColor(UIColor.purple.cgColor)
context.fillPath()
} else {
context.setStrokeColor(UIColor.red.cgColor)
context.strokePath()
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let position = touch.location(in: self)
print(position)
startPos = position
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let position = touch.location(in: self)
print(position)
endPos = position
setNeedsDisplay()
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let position = touch.location(in: self)
print(position)
endPos = position
setNeedsDisplay()
}
}
func addBackground() {
// screen width and height:
let width = UIScreen.main.bounds.size.width
let height = UIScreen.main.bounds.size.height
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: width, height: height))
imageView.image = UIImage(named: "img1.jpg")
// you can change the content mode:
imageView.contentMode = UIView.ContentMode.scaleAspectFill
self.addSubview(imageView)
self.sendSubviewToBack(imageView)
}
}
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var drawView: DrawView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
drawView.addBackground()
}
}
Im suspecting that I'm not using the correct UIGraphicsGetCurrentContext after setting the image but I was not able to figure it out how to fix it.

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:
and here is what I'm trying to achieve:
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
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()
}

Touch drawing not working when UIPanGestureRecognizer implemented for view

I have screen to test the device touch screen with popping bubbles. And some imageView added in subviews of bubbles made of cross for them. Then user swipe over the bubbles to check the touch screen.
And I want drawing on the same view. When user swipes the finger over the bubbles, a line will be drawn. I have separate class for drawing and assign it to main parent view of controller.
If I remove code for UIPanGestureRecognizer then drawing works and there are no lags.
If I add gesture to view for popping the bubbles like this
view.addGestureRecognizer(gestureRecognizer)
Then there is a lag, and drawing doesn't work.
I want both things like popping bubbles and drawing on view.
The main problem of this gesture is when I add this in view, then drawing works without any lag but popping bubbles doesn't work.
let gestureRecognizer : UIPanGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(panGestureRecognized(_:)))
gestureRecognizer.maximumNumberOfTouches = 1
gestureRecognizer.minimumNumberOfTouches = 1
view.addGestureRecognizer(gestureRecognizer)
Drawing view class
import UIKit
class DrawingView: UIView {
var drawColor = UIColor.black
var lineWidth: CGFloat = 5
private var lastPoint: CGPoint!
private var bezierPath: UIBezierPath!
private var pointCounter: Int = 0
private let pointLimit: Int = 128
private var preRenderImage: UIImage!
// MARK: - Initialization
override init(frame: CGRect) {
super.init(frame: frame)
initBezierPath()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initBezierPath()
}
func initBezierPath() {
bezierPath = UIBezierPath()
bezierPath.lineCapStyle = CGLineCap.round
bezierPath.lineJoinStyle = CGLineJoin.round
}
// MARK: - Touch handling
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch: AnyObject? = touches.first
lastPoint = touch!.location(in: self)
pointCounter = 0
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch: AnyObject? = touches.first
let newPoint = touch!.location(in: self)
bezierPath.move(to: lastPoint)
bezierPath.addLine(to: newPoint)
lastPoint = newPoint
pointCounter += 1
if pointCounter == pointLimit {
pointCounter = 0
renderToImage()
setNeedsDisplay()
bezierPath.removeAllPoints()
}
else {
setNeedsDisplay()
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
pointCounter = 0
renderToImage()
setNeedsDisplay()
bezierPath.removeAllPoints()
}
override func touchesCancelled(_ touches: Set<UITouch>?, with event: UIEvent?) {
touchesEnded(touches!, with: event)
}
// MARK: - Pre render
func renderToImage() {
UIGraphicsBeginImageContextWithOptions(self.bounds.size, false, 0.0)
if preRenderImage != nil {
preRenderImage.draw(in: self.bounds)
}
bezierPath.lineWidth = lineWidth
drawColor.setFill()
drawColor.setStroke()
bezierPath.stroke()
preRenderImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
}
// MARK: - Render
override func draw(_ rect: CGRect) {
super.draw(rect)
if preRenderImage != nil {
preRenderImage.draw(in: self.bounds)
}
bezierPath.lineWidth = lineWidth
drawColor.setFill()
drawColor.setStroke()
bezierPath.stroke()
}
// MARK: - Clearing
func clear() {
preRenderImage = nil
bezierPath.removeAllPoints()
setNeedsDisplay()
}
// MARK: - Other
func hasLines() -> Bool {
return preRenderImage != nil || !bezierPath.isEmpty
}
}

How to clear UIView?

I am working on a drawing app where the user draws something on a view. On the same view controller I would like to create a button that would erase everything that was on the view and give it a blank slate. How would I do this? I added all of my code that is attached to the uiview to draw the object.
import uikit
class ViewController: UIViewController {
#IBOutlet weak var jj: draw!
#IBAction func enterScore(_ sender: Any) {
(view as? drawViewSwift)?.clear()
}}
import UIKit
struct stroke {
let startPoint: CGPoint
let endPoint: CGPoint
let color: CGColor
}
class drawViewSwift: subClassOFUIVIEW {
var isDrawing = false
var lastPoint : CGPoint!
var strokeColor : CGColor = UIColor.black.cgColor
var storkes = [stroke]()
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard !isDrawing else {return}
isDrawing = true
guard let touch = touches.first else {return}
let currentPoint = touch.location(in: self)
lastPoint = currentPoint
setNeedsDisplay()
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard isDrawing else {return}
guard let touch = touches.first else {return}
setNeedsDisplay()
let currentPoint = touch.location(in: self)
print(currentPoint)
let sstroke = stroke(startPoint: lastPoint, endPoint: currentPoint, color: strokeColor)
storkes.append(sstroke)
lastPoint = currentPoint
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
guard isDrawing else {return}
isDrawing = false
guard let touch = touches.first else {return}
let currentPoint = touch.location(in: self)
let sstroke = stroke(startPoint: lastPoint, endPoint: currentPoint, color: strokeColor)
storkes.append(sstroke)
lastPoint = nil
setNeedsDisplay()
print(currentPoint)
}
private var clearing = false
func clear() {
clearing = true
setNeedsDisplay()
}
override func draw(_ rect: CGRect) {
super.draw(rect)
defer { setNeedsDisplay() }
let context = UIGraphicsGetCurrentContext()
guard !clearing else {
context?.clear(bounds)
context?.setFillColor(backgroundColor?.cgColor ?? UIColor.clear.cgColor)
context?.fill(bounds)
storkes.removeAll()
clearing = false
return
}
context?.setLineWidth(10)
context?.setLineCap(.round)
for stroke in storkes {
context?.beginPath()
context?.move(to:stroke.startPoint)
context?.addLine(to: stroke.endPoint)
context?.setStrokeColor(stroke.color)
context?.strokePath()
}
}
func crase() {
storkes = []
strokeColor = UIColor.black.cgColor
setNeedsDisplay()
}
}
class subClassOFUIVIEW: UIView {
override func awakeFromNib() {
layer.shadowOpacity = 1
layer.shadowOffset = CGSize(width: 1, height: 1)
layer.shadowPath = CGPath(rect: bounds, transform: nil)
layer.shadowPath = CGPath(rect: bounds, transform: nil)
}
}
class MyViewController : UIViewController {
override func loadView() {
let view = drawViewSwift()
view.backgroundColor = .white
let button = UIButton()
button.frame = CGRect(x: 150, y: 200, width: 200, height: 20)
button.setTitle("Clear", for: .normal)
button.setTitleColor(.blue, for: .normal)
button.addTarget(self, action: #selector(clear), for: .touchUpInside)
view.addSubview(button)
self.view = view
}
#objc func clear() {
(view as? drawViewSwift)?.clear()
}
}
You can add to your drawViewSwift class this code:
private var clearing = false
func clear() {
clearing = true
setNeedsDisplay()
}
At this point your drawRect should change to clear the view:
override func draw(_ rect: CGRect) {
super.draw(rect)
defer { setNeedsDisplay() }
let context = UIGraphicsGetCurrentContext()
guard !clearing else {
context?.clear(bounds)
context?.setFillColor(backgroundColor?.cgColor ?? UIColor.clear.cgColor)
context?.fill(bounds)
storkes.removeAll()
clearing = false
return
}
context?.setLineWidth(10)
context?.setLineCap(.round)
for stroke in storkes {
context?.beginPath()
context?.move(to:stroke.startPoint)
context?.addLine(to: stroke.endPoint)
context?.setStrokeColor(stroke.color)
context?.strokePath()
}
}
In the guard we will call the clerk method of the context which will reset the context to be at its initial state. This method will cause the context to redraw a transparent view. Since the view is now transparent we must redraw our canvas to be with a background color again. We do it by using the fill method after setting the fill color to be the background color of the view.
At this point all it's remaining to do is to clear our strokes array and switch back the value of clearing to false so that we are ready to draw again.
In your view controller, the clear button just have to call the clear method of this view.
Try it in playground:
//: A UIKit based Playground for presenting user interface
import UIKit
import PlaygroundSupport
struct stroke {
let startPoint: CGPoint
let endPoint: CGPoint
let color: CGColor
}
class drawViewSwift: subClassOFUIVIEW {
var isDrawing = false
var lastPoint : CGPoint!
var strokeColor : CGColor = UIColor.black.cgColor
var storkes = [stroke]()
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard !isDrawing else {return}
isDrawing = true
guard let touch = touches.first else {return}
let currentPoint = touch.location(in: self)
lastPoint = currentPoint
setNeedsDisplay()
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard isDrawing else {return}
guard let touch = touches.first else {return}
setNeedsDisplay()
let currentPoint = touch.location(in: self)
print(currentPoint)
let sstroke = stroke(startPoint: lastPoint, endPoint: currentPoint, color: strokeColor)
storkes.append(sstroke)
lastPoint = currentPoint
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
guard isDrawing else {return}
isDrawing = false
guard let touch = touches.first else {return}
let currentPoint = touch.location(in: self)
let sstroke = stroke(startPoint: lastPoint, endPoint: currentPoint, color: strokeColor)
storkes.append(sstroke)
lastPoint = nil
setNeedsDisplay()
print(currentPoint)
}
private var clearing = false
func clear() {
clearing = true
setNeedsDisplay()
}
override func draw(_ rect: CGRect) {
super.draw(rect)
defer { setNeedsDisplay() }
let context = UIGraphicsGetCurrentContext()
guard !clearing else {
context?.clear(bounds)
context?.setFillColor(backgroundColor?.cgColor ?? UIColor.clear.cgColor)
context?.fill(bounds)
storkes.removeAll()
clearing = false
return
}
context?.setLineWidth(10)
context?.setLineCap(.round)
for stroke in storkes {
context?.beginPath()
context?.move(to:stroke.startPoint)
context?.addLine(to: stroke.endPoint)
context?.setStrokeColor(stroke.color)
context?.strokePath()
}
}
func crase() {
storkes = []
strokeColor = UIColor.black.cgColor
setNeedsDisplay()
}
}
class subClassOFUIVIEW: UIView {
override func awakeFromNib() {
layer.shadowOpacity = 1
layer.shadowOffset = CGSize(width: 1, height: 1)
layer.shadowPath = CGPath(rect: bounds, transform: nil)
layer.shadowPath = CGPath(rect: bounds, transform: nil)
}
}
class MyViewController : UIViewController {
override func loadView() {
let view = drawViewSwift()
view.backgroundColor = .white
let button = UIButton()
button.frame = CGRect(x: 150, y: 200, width: 200, height: 20)
button.setTitle("Clear", for: .normal)
button.setTitleColor(.blue, for: .normal)
button.addTarget(self, action: #selector(clear), for: .touchUpInside)
view.addSubview(button)
self.view = view
}
#objc func clear() {
(view as? drawViewSwift)?.clear()
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
Your code to draw and to clear works. You should correct your viewController code and the layout structure you have choose. I think it's better to make a view dedicated to draw, so for example you could have 3 different objects to the main view, one for your label, another for your button and the last as your drawViewSwift. With this structure your main view can handle the constraints between objects and the other objects don't disturb the drawing (and obviusly the cleaning..):
Layout:
Output:
If number of strikes are high - Recommanded
If your strokes array is too big. I recommend to use below code
let rectangle = CGRect(x: 0, y: 0, width: self.height, height: self.height)
CGContextSetFillColorWithColor(context, UIColor.white.CGColor)
CGContextAddRect(context, rectangle)
CGContextDrawPath(context, .Fill)
If user hasn't drawn many strikes
Since you have all the current drawn strokes inside array strokes. below method will also work.
Just redraw above thus strokes with your view background color
override func clearView() {
let context = UIGraphicsGetCurrentContext()
context?.setLineWidth(10)
context?.setLineCap(.round)
for stroke in storkes {
context?.beginPath()
context?.move(to:stroke.startPoint)
context?.addLine(to: stroke.endPoint)
context?.setStrokeColor(UIColor.white.cgColor)
// Assuming your background color is white
context?.strokePath()
}
}

Resources