I have UIView Canvas
to draw lines.I am trying to clear the Canvas background color and show the image in the background each time the user draws a line. when I draw a line on Canvas, is not applying clear white background color. how do I solve the issue?
import UIKit
class Canvas: UIView{
var lines = [[CGPoint]]()
override func draw(_ rect:CGRect){
super.draw(rect)
guard let context = UIGraphicsGetCurrentContext() else {
return
}
context.setStrokeColor(UIColor.clear.cgColor)
context.setLineWidth(20)
context.setLineCap( .butt)
lines.forEach{ (line) in
for (i,p) in line.enumerated(){
if i == 0{
context.move(to: p)
} else {
context.addLine(to: p)
}
}
}
context.strokePath()
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
lines.append([CGPoint]())
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let point = touches.first?.location(in:nil) else {
return
}
guard var lastLine = lines.popLast() else { return }
lastLine.append(point)
lines.append(lastLine)
setNeedsDisplay()
}
}
class ViewController: UIViewController {
let canvas = Canvas()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(canvas)
canvas.backgroundColor = .white
canvas.frame = view.frame
}
}
CodePudding user response:
The trick is to draw the line paths with a special blend mode of clear. Here is an updated draw
method that also makes use of UIBezierPath
to make it a bit easier.
override func draw(_ rect: CGRect) {
// Fill the canvas with the white background
UIColor.white.set()
let fill = UIBezierPath(rect: bounds)
fill.fill()
// Build a path with all of the lines
let path = UIBezierPath()
path.lineWidth = 20
path.lineCapStyle = .butt
for line in lines {
for (i,p) in line.enumerated() {
if i == 0 {
path.move(to: p)
} else {
path.addLine(to: p)
}
}
}
// Draw the path with a clear color and a clear blending mode
// This erases the white background showing whatever is behind the canvas
UIColor.clear.set()
path.stroke(with: .clear, alpha: 1)
}
You also need to remove the setting of the canvas view's backgroundColor
property. Do not set it to any color.
Lastly, without any further changes you will end up with black lines instead of "erased" lines. The last change to fix this is to set isOpaque
to false
on the canvas view.
You can simply do:
canvas.isOpaque = false
in the view controller code but it's better to set this in the Canvas
class. Do this by overriding the init
methods.
override init(frame: CGRect) {
super.init(frame: frame)
isOpaque = false
}
required init?(coder: NSCoder) {
super.init(coder: coder)
isOpaque = false
}
Here's the updated viewDidLoad
:
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(canvas)
canvas.frame = view.frame
}
If you want to get fancy you can override backgroundColor
in Canvas
. This allows the creator of the canvas to set the desired background color but ensures that the actual canvas keeps a clear background color and uses the set color as the fill color.
private var fillColor: UIColor?
override var backgroundColor: UIColor? {
get {
return fillColor
}
set {
fillColor = newValue
}
}
Then update draw
to use fillColor
instead of hardcoding white
.
(fillColor ?? .clear).set()
You might want a default color other than .clear
.