PencilKit: zoom PKCanvasView, drawing blurred (swift, iOS 15)

It seems PKCanvasView overrides the property UIScrollViewDelegate which inherits from the UIScrollView to PKCanvasViewDelegate. And does not provide access to UIScrollViewDelegate.

In order to implement zooming, so I added a PKCanvasView into my own UIScrollView. And implemented delegate method viewForZooming in which return PKCanvasView.

But all drawing in PKCanvasView was blurred when zooming or scale. How to re-render drawing after zoom to make it has reasonable stroke width and clear ?

Some related code:

let canvasView = PKCanvasView()
let scrollView = UIScrollView()

override func viewDidLoad() {
    super.viewDidLoad()

    self.view.addSubview(scrollView)
    scrollView.addSubview(canvasView)
    scrollView.delegate = self
    scrollView.minimumZoomScale = 0.5
    scrollView.maximumZoomScale = 2.5
}

func viewForZooming(in scrollView: UIScrollView) -> UIView? {
    return canvasView
}

Some solutions I had tried:

1: Reset PKCanvasView contentScaleFactor

func scrollViewDidEndZooming(_ scrollView: UIScrollView, with view: UIView?, atScale scale: CGFloat) {
    if let canvas = view {
      let contentScale = scale * UIScreen.main.scale
      canvas.contentScaleFactor = contentScale
    }
  }

Not worked!

2: Re-render PKStroke:

func reRender(_ scale: CGFloat) {
    let newStrokeWidth = strokeWidth * scale
    var newDrawingStrokes: [PKStroke] = []
    for stroke in canvasView.drawing.strokes {
      canvasView.tool = PKInkingTool(.pen, color: .red, width: newStrokeWidth)
      var newPoints = [PKStrokePoint]()
      stroke.path.forEach { (point) in
        let newPoint = PKStrokePoint(location: point.location,
                       timeOffset: point.timeOffset,
                       size: CGSize(width: newStrokeWidth, height: newStrokeWidth),
                       opacity: CGFloat(1), force: point.force,
                       azimuth: point.azimuth, altitude: point.altitude)
        newPoints.append(newPoint)
      }
      let newPath = PKStrokePath(controlPoints: newPoints, creationDate: Date())
      let newStroke = PKStroke(ink: PKInk(.pen, color: UIColor.red), path: newPath)
      newDrawingStrokes.append(newStroke)
    }
    let newDrawing = PKDrawing(strokes: newDrawingStrokes)
    canvasView.drawing = newDrawing
  }

Not worked! Still blurred, just changed strokeWidth by multiply scale.

3: I try to reset PKDrawing or PKStroke transform by using scrollView scale. Then PKDrawing position disordered and it was still blurred.

Please help me.

Post not yet marked as solved Up vote post of M-Three Down vote post of M-Three
4.2k views
  • Correct some my descriptions (more detail): Correct: PKCanvasViewDelegate inherits UIScrollViewDelegate, So it can conform all UIScrollViewDelegate's methods.

    However, I need to add PKCanvasView to another view(PDFView's documentView), so that can doodle on PDFDocument. I had already achieved it. But still when zooming PDFView, my PKCanvasView that as subview of PDFView's documentView was scaled too. Then the drawing was blurred. How to fix it?

Add a Comment

Replies

I literally have the exact same issue! I'm trying to zoom in a drawing without making it pixelated, but so far I haven't come across any solutions :(

  1. canvasView.minimumZoomScale = scrollView.minimumZoomScale
  2. canvasView.maximumZoomScale = scrollView.maximumZoomScale
  3. func scrollViewDidZoom(_ scrollView: UIScrollView) { canvasView.zoomScale = scrollView.zoomScale }

I'm also struggling with this issue, Did you figure out any solution?

I am also struggling with this issue. I created FB13286723. As this exists since iOS 15 up to iOS 17 and is even present when opening and annotating a PDF from e.g. the Apple Files app (open a PDF from there, activate annotation mode, zoom in, draw => blurry) I have not much hope that Apple will address this 😭

If anyone found a workaround, please share.