I recently ventured into utilizing Swift's UIKit to create a custom PencilKit.
My approach involves sampling information about points every time the pencil touches the screen, storing this data in a format like
strokeCollection = {strokes: [Stroke], drawingStroke: Stroke?}
and overriding the UIView's draw method to redraw the view each time the strokeCollection is updated. However, as more strokes are drawn, the app begins to significantly slow down due to the need to redraw every stroke continually.
In React Native Skia, I've used the "createPicture" method to leverage the "Picture" component, keeping previously drawn strokes intact and preventing the app from slowing down. I'm curious if there's a similar technique available in Swift or if you might have other suggestions to address this issue.
My original code draws for every stroke every time as below.
override func draw(_ rect: CGRect) {
UIColor.clear.set()
UIRectFill(rect)
// saved strokes
if let strokeCollection = strokeCollection {
if strokeToDraw != nil {
for stroke in strokeCollection.strokes {
draw(stroke: stroke, in: rect)
}
}
}
// currently drawing stroke
if let stroke = strokeToDraw {
draw(stroke: stroke, in: rect)
}
}
func draw(stroke: Stroke, in rect: CGRect) {
guard stroke.samples.isEmpty == false else { return }
let path = UIBezierPath()
var previousSample: StrokeSample?
for sample in stroke.samples {
if let prev = previousSample {
let midPoint = midpoint(first: prev.location, second: sample.location)
path.addQuadCurve(to: midPoint, controlPoint: prev.location)
} else {
path.move(to: sample.location)
}
previousSample = sample
}
if let lastSample = previousSample {
path.addLine(to: lastSample.location)
}
if let context = UIGraphicsGetCurrentContext() {
switch stroke.type {
case .pen, .marker:
context.setBlendMode(.normal)
context.addPath(path.cgPath)
context.setStrokeColor(stroke.color.cgColor)
context.setLineWidth(stroke.width * 2)
context.setAlpha(stroke.opacity)
context.setLineCap(.round)
context.setLineJoin(.round)
context.strokePath()
case .eraser:
context.setBlendMode(.clear)
context.addPath(path.cgPath)
context.setStrokeColor(stroke.color.cgColor)
context.setLineWidth(stroke.width * 2)
context.setAlpha(stroke.opacity)
context.setLineCap(.round)
context.setLineJoin(.round)
context.strokePath()
}
}
}