PencilKit
Integrate drawing, sketching, and annotation into iOS and iPadOS apps using PencilKit (PKCanvasView, PKToolPicker, PKDrawing) and PaperKit. Targets Swift 6.3 / iOS 26+.
Contents
Core PencilKit Architecture
| Component |
Responsibility |
PKCanvasView |
Scrollable drawing canvas that receives touch and pencil inputs; inherits from UIScrollView |
PKToolPicker |
Floating system tool palette offering pens, pencils, markers, erasers, rulers, and custom tools |
PKDrawing |
Immutable data model containing vector strokes (PKStroke), stroke points, and bounds |
PKTool |
Active drawing instrument (PKInkingTool, PKEraserTool, PKLassoTool) |
PKCanvasViewDelegate |
Notifies when drawing changes or user starts/ends drawing |
SwiftUI Integration Pattern
Bridge PKCanvasView into SwiftUI using UIViewRepresentable:
import SwiftUI
import PencilKit
struct CanvasViewRepresentable: UIViewRepresentable {
@Binding var drawing: PKDrawing
var tool: PKTool = PKInkingTool(.pen, color: .black, width: 5)
var isRulerActive: Bool = false
func makeUIView(context: Context) -> PKCanvasView {
let canvas = PKCanvasView()
canvas.drawingPolicy = .anyInput
canvas.tool = tool
canvas.isRulerActive = isRulerActive
canvas.delegate = context.coordinator
return canvas
}
func updateUIView(_ uiView: PKCanvasView, context: Context) {
if uiView.drawing != drawing {
uiView.drawing = drawing
}
uiView.tool = tool
uiView.isRulerActive = isRulerActive
}
func makeCoordinator() -> Coordinator { Coordinator(self) }
class Coordinator: NSObject, PKCanvasViewDelegate {
var parent: CanvasViewRepresentable
init(_ parent: CanvasViewRepresentable) { self.parent = parent }
func canvasViewDrawingDidChange(_ canvasView: PKCanvasView) {
Task { @MainActor in parent.drawing = canvasView.drawing }
}
}
}
Drawing Persistence and Data
- Serialization: Serialize drawings using
drawing.dataRepresentation(). Restore via PKDrawing(data:).
- Image Generation: Render drawings to raster images with
drawing.image(from: canvasView.bounds, scale: canvasView.traitCollection.displayScale).
- Stroke Inspection: Iterate through
drawing.strokes to inspect points, pressure, force, and azimuth.
Input Policy and Gestures
Configure canvasView.drawingPolicy:
.default: Follows system preference (Apple Pencil only if configured in Settings).
.anyInput: Allows finger drawing alongside Apple Pencil.
.pencilOnly: Restricts drawing strictly to Apple Pencil; finger touches scroll the canvas.
Coordinating PKToolPicker: Attach the picker with toolPicker.setVisible(true, forFirstResponder: canvasView) and toolPicker.addObserver(canvasView). Ensure the canvas becomes first responder.
Route by Task
- For tracking tool picker changes, visibility, and frame obstruction, read Tool Picker Observer Pattern.
- For custom items in
PKToolPicker (iOS 18+), read Custom Tool Picker Items.
- For stroke construction, comparison, and shape recognition, read Constructing Strokes Programmatically.
- For thumbnail generation and background image rendering, read Thumbnail Generation.
- For undo/redo coordination with
UndoManager, read Undo/Redo Support.
Common Mistakes
- Forgetting to call
canvasView.becomeFirstResponder() before showing PKToolPicker.
- Updating SwiftUI
@Binding var drawing continuously during drawing gestures, causing hitching and feedback loops.
- Overriding finger scrolling gestures without setting
drawingPolicy = .pencilOnly.
- Generating high-resolution raster images synchronously on the main thread from complex drawings.
- Assuming
PKToolPicker floats automatically in SwiftUI without anchoring to a window or first responder.
Review Checklist
References
1---2name: pencilkit3description: Implement drawing, handwriting, and sketching with PencilKit and PaperKit. Use when integrating PKCanvasView, configuring PKToolPicker, handling drawing gestures and Apple Pencil features, inspecting or rendering strokes, managing drawing data, or building drawing interfaces.4---56# PencilKit78Integrate drawing, sketching, and annotation into iOS and iPadOS apps using `PencilKit` (`PKCanvasView`, `PKToolPicker`, `PKDrawing`) and `PaperKit`. Targets Swift 6.3 / iOS 26+.910## Contents1112- [Core PencilKit Architecture](#core-pencilkit-architecture)13- [SwiftUI Integration Pattern](#swiftui-integration-pattern)14- [Drawing Persistence and Data](#drawing-persistence-and-data)15- [Input Policy and Gestures](#input-policy-and-gestures)16- [Route by Task](#route-by-task)17- [Common Mistakes](#common-mistakes)18- [Review Checklist](#review-checklist)19- [References](#references)2021## Core PencilKit Architecture2223| Component | Responsibility |24|---|---|25| `PKCanvasView` | Scrollable drawing canvas that receives touch and pencil inputs; inherits from `UIScrollView` |26| `PKToolPicker` | Floating system tool palette offering pens, pencils, markers, erasers, rulers, and custom tools |27| `PKDrawing` | Immutable data model containing vector strokes (`PKStroke`), stroke points, and bounds |28| `PKTool` | Active drawing instrument (`PKInkingTool`, `PKEraserTool`, `PKLassoTool`) |29| `PKCanvasViewDelegate` | Notifies when drawing changes or user starts/ends drawing |3031## SwiftUI Integration Pattern3233Bridge `PKCanvasView` into SwiftUI using `UIViewRepresentable`:3435```swift36import SwiftUI37import PencilKit3839struct CanvasViewRepresentable: UIViewRepresentable {40 @Binding var drawing: PKDrawing41 var tool: PKTool = PKInkingTool(.pen, color: .black, width: 5)42 var isRulerActive: Bool = false4344 func makeUIView(context: Context) -> PKCanvasView {45 let canvas = PKCanvasView()46 canvas.drawingPolicy = .anyInput47 canvas.tool = tool48 canvas.isRulerActive = isRulerActive49 canvas.delegate = context.coordinator50 return canvas51 }5253 func updateUIView(_ uiView: PKCanvasView, context: Context) {54 if uiView.drawing != drawing {55 uiView.drawing = drawing56 }57 uiView.tool = tool58 uiView.isRulerActive = isRulerActive59 }6061 func makeCoordinator() -> Coordinator { Coordinator(self) }6263 class Coordinator: NSObject, PKCanvasViewDelegate {64 var parent: CanvasViewRepresentable65 init(_ parent: CanvasViewRepresentable) { self.parent = parent }6667 func canvasViewDrawingDidChange(_ canvasView: PKCanvasView) {68 Task { @MainActor in parent.drawing = canvasView.drawing }69 }70 }71}72```7374## Drawing Persistence and Data7576- **Serialization**: Serialize drawings using `drawing.dataRepresentation()`. Restore via `PKDrawing(data:)`.77- **Image Generation**: Render drawings to raster images with `drawing.image(from: canvasView.bounds, scale: canvasView.traitCollection.displayScale)`.78- **Stroke Inspection**: Iterate through `drawing.strokes` to inspect points, pressure, force, and azimuth.7980## Input Policy and Gestures8182Configure `canvasView.drawingPolicy`:83- `.default`: Follows system preference (Apple Pencil only if configured in Settings).84- `.anyInput`: Allows finger drawing alongside Apple Pencil.85- `.pencilOnly`: Restricts drawing strictly to Apple Pencil; finger touches scroll the canvas.8687Coordinating `PKToolPicker`: Attach the picker with `toolPicker.setVisible(true, forFirstResponder: canvasView)` and `toolPicker.addObserver(canvasView)`. Ensure the canvas becomes first responder.8889## Route by Task9091- For tracking tool picker changes, visibility, and frame obstruction, read [Tool Picker Observer Pattern](references/pencilkit-patterns.md#tool-picker-observer-pattern).92- For custom items in `PKToolPicker` (iOS 18+), read [Custom Tool Picker Items](references/pencilkit-patterns.md#custom-tool-picker-items).93- For stroke construction, comparison, and shape recognition, read [Constructing Strokes Programmatically](references/pencilkit-patterns.md#constructing-strokes-programmatically).94- For thumbnail generation and background image rendering, read [Thumbnail Generation](references/pencilkit-patterns.md#thumbnail-generation).95- For undo/redo coordination with `UndoManager`, read [Undo/Redo Support](references/pencilkit-patterns.md#undoredo-support).9697## Common Mistakes9899- Forgetting to call `canvasView.becomeFirstResponder()` before showing `PKToolPicker`.100- Updating SwiftUI `@Binding var drawing` continuously during drawing gestures, causing hitching and feedback loops.101- Overriding finger scrolling gestures without setting `drawingPolicy = .pencilOnly`.102- Generating high-resolution raster images synchronously on the main thread from complex drawings.103- Assuming `PKToolPicker` floats automatically in SwiftUI without anchoring to a window or first responder.104105## Review Checklist106107- [ ] `PKCanvasView` embedded in SwiftUI with a clean Coordinator108- [ ] `drawingPolicy` explicitly set (`.anyInput`, `.pencilOnly`, or `.default`)109- [ ] `PKToolPicker` tied to canvas first responder and visible state110- [ ] Drawing data serialized with `dataRepresentation()` and restored with error handling111- [ ] Raster image rendering performed asynchronously or off the main thread for large canvases112- [ ] Undo and redo actions integrated with the canvas's `undoManager`113- [ ] Dynamic Type and safe area insets respected when `PKToolPicker` obscures canvas regions114115## References116117- [PencilKit extended patterns and stroke inspection](references/pencilkit-patterns.md)118- [PencilKit documentation](https://sosumi.ai/documentation/pencilkit)119- [PKCanvasView](https://sosumi.ai/documentation/pencilkit/pkcanvasview)120- [PKToolPicker](https://sosumi.ai/documentation/pencilkit/pktoolpicker)121- [PKDrawing](https://sosumi.ai/documentation/pencilkit/pkdrawing)