dpearson2699/swift-ios-skills3 files

Pencilkit

Add 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 Pencil-powered experiences on iOS/iPadOS/visionOS.

Specification
Skill ID
dpearson2699/swift-ios-skills/pencilkit
Publisher
dpearson2699
Repository
swift-ios-skills
Installs
0
Files
3
Synced
Sep 23, 2026
How to use it

Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.

dpearson2699/swift-ios-skills/pencilkitInstalls these files
  • SKILL.md
  • evals/evals.json
  • references/pencilkit-patterns.md

What this skill tells the agent

PencilKit

Capture Apple Pencil and finger input using PKCanvasView, manage drawing tools with PKToolPicker, serialize drawings with PKDrawing, and wrap PencilKit in SwiftUI.

Contents

  • Setup
  • Capture-to-Export Workflow
  • PKCanvasView Basics
  • PKToolPicker
  • PKDrawing Serialization
  • Content Version Compatibility
  • Exporting to Image
  • Stroke Inspection
  • SwiftUI Integration
  • PaperKit Relationship
  • Common Mistakes
  • Review Checklist
  • References

Setup

PencilKit requires no entitlements or Info.plist entries. Import PencilKit and create a PKCanvasView.

import PencilKit

Platform availability: iOS 13+, iPadOS 13+, Mac Catalyst 13.1+, visionOS 1.0+.

Capture-to-Export Workflow

  1. Capture: Read canvasView.drawing from canvasViewDrawingDidChange(_:); keep the previous persisted revision until the new revision completes the remaining checkpoints.
  2. Serialize: Create dataRepresentation(), write atomically, and run the decode validate/fix/retry loop. Do not mark bytes valid when PKDrawing(data:) still throws.
  3. Version-gate: Apply Content Version Compatibility before editable sync. If the recipient cannot load the drawing, preserve the full-fidelity source and use an existing compatible fallback or read-only preview.
  4. Sync: Send only validated, compatible data and mark the revision synced after acknowledgement. On transport or conflict failure, retain the pending revision, resolve the cause, and retry without discarding the last good copy.
  5. Export: Validate a nonempty drawing region and intended scale before calling image(from:scale:); skip export on invalid bounds without altering the serialized drawing.

PKCanvasView Basics

PKCanvasView is a UIScrollView subclass that captures Apple Pencil and finger input and renders strokes.

import PencilKit
import UIKit

class DrawingViewController: UIViewController, PKCanvasViewDelegate {
    let canvasView = PKCanvasView()

    override func viewDidLoad() {
        super.viewDidLoad()
        canvasView.delegate = self
        canvasView.drawingPolicy = .anyInput
        canvasView.tool = PKInkingTool(.pen, color: .black, width: 5)
        canvasView.frame = view.bounds
        canvasView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        view.addSubview(canvasView)
    }

    func canvasViewDrawingDidChange(_ canvasView: PKCanvasView) {
        // Drawing changed -- save or process
    }
}

Drawing Policies

PolicyBehavior
.defaultRespects UIPencilInteraction.prefersPencilOnlyDrawing when the tool picker is visible; otherwise Pencil-only
.anyInputBoth pencil and finger draw
.pencilOnlyOnly Apple Pencil touches draw on the canvas
canvasView.drawingPolicy = .pencilOnly

Use .default for system-standard Pencil-primary canvases when the tool picker's drawing-policy control should follow the user's Pencil preference. Use .anyInput for signature pads, whiteboards, or explicit finger-drawing modes. Use .pencilOnly when finger input should never create strokes.

Configuring the Canvas

// Set a large drawing area (scrollable)
canvasView.contentSize = CGSize(width: 2000, height: 3000)

// Enable/disable the ruler
canvasView.isRulerActive = true

// Set the current tool programmatically
canvasView.tool = PKInkingTool(.pencil, color: .blue, width: 3)
canvasView.tool = PKEraserTool(.vector)

PKToolPicker

PKToolPicker displays a floating palette of drawing tools. The canvas automatically adopts the selected tool.

class DrawingViewController: UIViewController {
    let canvasView = PKCanvasView()
    let toolPicker = PKToolPicker()

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        toolPicker.addObserver(canvasView)
        toolPicker.setVisible(true, forFirstResponder: canvasView)
        canvasView.becomeFirstResponder()
    }
}

Custom Tool Picker Items

Create a tool picker with specific tools. PKToolPicker(toolItems:) and custom tool picker item classes require iOS/iPadOS 18+, Mac Catalyst 18+, and visionOS 2+; those item classes are available on macOS starting in macOS 26.

let toolPicker = PKToolPicker(toolItems: [
    PKToolPickerInkingItem(type: .pen, color: .black, width: 5),
    PKToolPickerInkingItem(type: .pencil, color: .gray, width: 5),
    PKToolPickerInkingItem(type: .marker, color: .yellow, width: 12),
    PKToolPickerEraserItem(type: .vector),
    PKToolPickerLassoItem(),
    PKToolPickerRulerItem()
])

Ink Types

TypeDescription
.penSmooth, pressure-sensitive pen
.pencilTextured pencil with tilt shading
.markerSemi-transparent highlighter
.monolineUniform-width pen
.fountainPenVariable-width calligraphy pen
.watercolorBlendable watercolor brush
.crayonTextured crayon
.reedReed pen (iOS/iPadOS/macOS/visionOS 26+)

Content Versions

Use Content Version Compatibility as the single version map and compatibility gate for both the canvas and tool picker.

PKDrawing Serialization

PKDrawing is a value type (struct) that holds all stroke data. Serialize it to Data for persistence.

// Save
func saveDrawing(_ drawing: PKDrawing) throws {
    let data = drawing.dataRepresentation()
    try data.write(to: fileURL, options: .atomic)
}

// Load
func loadDrawing() throws -> PKDrawing {
    let data = try Data(contentsOf: fileURL)
    return try PKDrawing(data: data)
}

Decode Validate/Fix/Retry Loop

For synced or user-provided data: validate with PKDrawing(data:); on failure preserve the original bytes and fix the cause by refetching an intact revision or selecting a previously generated compatible copy; then retry the decode. Assign the drawing only after a successful retry. If recovery still fails, keep the source unchanged and show an error or available read-only preview instead of suppressing the failure with try?.

do {
    canvasView.drawing = try PKDrawing(data: correctedData) // retry
} catch {
    showReadOnlyPreview(for: document, loadError: error)
}

Combining Drawings

var drawing1 = PKDrawing()
let drawing2 = PKDrawing()
drawing1.append(drawing2)

// Non-mutating
let combined = drawing1.appending(drawing2)

Transforming Drawings

let scaled = drawing.transformed(using: CGAffineTransform(scaleX: 2, y: 2))
let translated = drawing.transformed(using: CGAffineTransform(translationX: 100, y: 0))

Content Version Compatibility

For sync, migration, downgrade, or cross-device editing tasks, use requiredContentVersion as the compatibility gate and choose an explicit maximumSupportedContentVersion when old clients must keep editing.

let targetVersion: PKContentVersion = .version1
canvasView.maximumSupportedContentVersion = targetVersion
toolPicker.maximumSupportedContentVersion = targetVersion

switch drawing.requiredContentVersion {
case .version1:
    // Older marker, pen, and pencil ink set
    syncEditable(drawing)
case .version2:
    // iPadOS 17-era inks: monoline, fountain pen, watercolor, crayon
    syncIfRecipientsSupportVersion2(drawing)
case .version3, .version4:
    // Later features such as barrel-roll data and Reed Pen
    syncEditableOnlyToCurrentClients(drawing)
@unknown default:
    showReadOnlyPreview(for: drawing)
}

If a drawing requires a newer version than a recipient can load, preserve the

Use this skill in a project

Open any RiverX project, search the Skills panel for dpearson2699/swift-ios-skills/pencilkit, and install it in one click.

Use this skill