$npx -y skills add dpearson2699/swift-ios-skills --skill pencilkitAdd Apple Pencil drawing with PKCanvasView, PKToolPicker, PKDrawing serialization/export, stroke inspection, and PencilKit/PaperKit handoffs. Use when building drawing apps, annotation features, handwriting capture, signature fields, content-version-safe ink workflows, or Apple P
| 1 | # PencilKit |
| 2 | |
| 3 | Capture Apple Pencil and finger input using `PKCanvasView`, manage drawing |
| 4 | tools with `PKToolPicker`, serialize drawings with `PKDrawing`, and wrap PencilKit in SwiftUI. |
| 5 | |
| 6 | ## Contents |
| 7 | |
| 8 | - [Setup](#setup) |
| 9 | - [Capture-to-Export Workflow](#capture-to-export-workflow) |
| 10 | - [PKCanvasView Basics](#pkcanvasview-basics) |
| 11 | - [PKToolPicker](#pktoolpicker) |
| 12 | - [PKDrawing Serialization](#pkdrawing-serialization) |
| 13 | - [Content Version Compatibility](#content-version-compatibility) |
| 14 | - [Exporting to Image](#exporting-to-image) |
| 15 | - [Stroke Inspection](#stroke-inspection) |
| 16 | - [SwiftUI Integration](#swiftui-integration) |
| 17 | - [PaperKit Relationship](#paperkit-relationship) |
| 18 | - [Common Mistakes](#common-mistakes) |
| 19 | - [Review Checklist](#review-checklist) |
| 20 | - [References](#references) |
| 21 | |
| 22 | ## Setup |
| 23 | |
| 24 | PencilKit requires no entitlements or Info.plist entries. Import `PencilKit` |
| 25 | and create a `PKCanvasView`. |
| 26 | |
| 27 | ```swift |
| 28 | import PencilKit |
| 29 | ``` |
| 30 | |
| 31 | **Platform availability:** iOS 13+, iPadOS 13+, Mac Catalyst 13.1+, visionOS 1.0+. |
| 32 | |
| 33 | ## Capture-to-Export Workflow |
| 34 | |
| 35 | 1. **Capture:** Read `canvasView.drawing` from |
| 36 | `canvasViewDrawingDidChange(_:)`; keep the previous persisted revision until |
| 37 | the new revision completes the remaining checkpoints. |
| 38 | 2. **Serialize:** Create `dataRepresentation()`, write atomically, and run the |
| 39 | [decode validate/fix/retry loop](#decode-validatefixretry-loop). Do not mark |
| 40 | bytes valid when `PKDrawing(data:)` still throws. |
| 41 | 3. **Version-gate:** Apply [Content Version Compatibility](#content-version-compatibility) |
| 42 | before editable sync. If the recipient cannot load the drawing, preserve the |
| 43 | full-fidelity source and use an existing compatible fallback or read-only |
| 44 | preview. |
| 45 | 4. **Sync:** Send only validated, compatible data and mark the revision synced |
| 46 | after acknowledgement. On transport or conflict failure, retain the pending |
| 47 | revision, resolve the cause, and retry without discarding the last good copy. |
| 48 | 5. **Export:** Validate a nonempty drawing region and intended scale before |
| 49 | calling `image(from:scale:)`; skip export on invalid bounds without altering |
| 50 | the serialized drawing. |
| 51 | |
| 52 | ## PKCanvasView Basics |
| 53 | |
| 54 | `PKCanvasView` is a `UIScrollView` subclass that captures Apple Pencil and |
| 55 | finger input and renders strokes. |
| 56 | |
| 57 | ```swift |
| 58 | import PencilKit |
| 59 | import UIKit |
| 60 | |
| 61 | class DrawingViewController: UIViewController, PKCanvasViewDelegate { |
| 62 | let canvasView = PKCanvasView() |
| 63 | |
| 64 | override func viewDidLoad() { |
| 65 | super.viewDidLoad() |
| 66 | canvasView.delegate = self |
| 67 | canvasView.drawingPolicy = .anyInput |
| 68 | canvasView.tool = PKInkingTool(.pen, color: .black, width: 5) |
| 69 | canvasView.frame = view.bounds |
| 70 | canvasView.autoresizingMask = [.flexibleWidth, .flexibleHeight] |
| 71 | view.addSubview(canvasView) |
| 72 | } |
| 73 | |
| 74 | func canvasViewDrawingDidChange(_ canvasView: PKCanvasView) { |
| 75 | // Drawing changed -- save or process |
| 76 | } |
| 77 | } |
| 78 | ``` |
| 79 | |
| 80 | ### Drawing Policies |
| 81 | |
| 82 | | Policy | Behavior | |
| 83 | |---|---| |
| 84 | | `.default` | Respects `UIPencilInteraction.prefersPencilOnlyDrawing` when the tool picker is visible; otherwise Pencil-only | |
| 85 | | `.anyInput` | Both pencil and finger draw | |
| 86 | | `.pencilOnly` | Only Apple Pencil touches draw on the canvas | |
| 87 | |
| 88 | ```swift |
| 89 | canvasView.drawingPolicy = .pencilOnly |
| 90 | ``` |
| 91 | |
| 92 | Use `.default` for system-standard Pencil-primary canvases when the tool |
| 93 | picker's drawing-policy control should follow the user's Pencil preference. Use |
| 94 | `.anyInput` for signature pads, whiteboards, or explicit finger-drawing modes. |
| 95 | Use `.pencilOnly` when finger input should never create strokes. |
| 96 | |
| 97 | ### Configuring the Canvas |
| 98 | |
| 99 | ```swift |
| 100 | // Set a large drawing area (scrollable) |
| 101 | canvasView.contentSize = CGSize(width: 2000, height: 3000) |
| 102 | |
| 103 | // Enable/disable the ruler |
| 104 | canvasView.isRulerActive = true |
| 105 | |
| 106 | // Set the current tool programmatically |
| 107 | canvasView.tool = PKInkingTool(.pencil, color: .blue, width: 3) |
| 108 | canvasView.tool = PKEraserTool(.vector) |
| 109 | ``` |
| 110 | |
| 111 | ## PKToolPicker |
| 112 | |
| 113 | `PKToolPicker` displays a floating palette of drawing tools. The canvas |
| 114 | automatically adopts the selected tool. |
| 115 | |
| 116 | ```swift |
| 117 | class DrawingViewController: UIViewController { |
| 118 | let canvasView = PKCanvasView() |
| 119 | let toolPicker = PKToolPicker() |
| 120 | |
| 121 | override func viewDidAppear(_ animated: Bool) { |
| 122 | super.viewDidAppear(animated) |
| 123 | toolPicker.addObserver(canvasView) |
| 124 | toolPicker.setVisible(true, forFirstResponder: canvasView) |
| 125 | canvasView.becomeFirstResponder() |
| 126 | } |
| 127 | } |
| 128 | ``` |
| 129 | |
| 130 | ### Custom Tool Picker Items |
| 131 | |
| 132 | Create a tool picker with specific tools. `PKToolPicker(toolItems:)` and |
| 133 | custom tool picker item classes require iOS/iPadOS 18+, Mac Catalyst 18+, and |
| 134 | visionOS 2+; those item classes are available on macOS starting |