@mmerterden/multi-agent-pipeline 20.7.0 → 20.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/LICENSE +0 -10
- package/docs/facts.json +1 -1
- package/manifest.json +266 -267
- package/package.json +2 -2
- package/pipeline/scripts/_notices.mjs +1 -1
- package/pipeline/skills/.skill-manifest.json +68 -68
- package/pipeline/skills/shared/README.md +70 -70
- package/pipeline/skills/shared/external/alarmkit/SKILL.md +373 -381
- package/pipeline/skills/shared/external/alarmkit/evals/evals.json +23 -18
- package/pipeline/skills/shared/external/alarmkit/references/alarmkit-patterns.md +328 -378
- package/pipeline/skills/shared/external/app-clips/SKILL.md +260 -160
- package/pipeline/skills/shared/external/app-clips/evals/evals.json +27 -27
- package/pipeline/skills/shared/external/app-clips/references/data-handoff-notifications-location.md +150 -83
- package/pipeline/skills/shared/external/app-clips/references/routing-and-experiences.md +135 -83
- package/pipeline/skills/shared/external/app-clips/references/size-capabilities-and-promotion.md +143 -85
- package/pipeline/skills/shared/external/app-intents/SKILL.md +302 -304
- package/pipeline/skills/shared/external/app-intents/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +594 -894
- package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +339 -277
- package/pipeline/skills/shared/external/app-store-optimization/evals/evals.json +27 -23
- package/pipeline/skills/shared/external/app-store-optimization/references/keyword-research-methodology.md +105 -122
- package/pipeline/skills/shared/external/app-store-optimization/references/product-page-variants.md +143 -166
- package/pipeline/skills/shared/external/app-store-review/SKILL.md +307 -326
- package/pipeline/skills/shared/external/app-store-review/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/app-store-review/references/privacy-manifest.md +105 -67
- package/pipeline/skills/shared/external/app-store-review/references/review-checklists.md +114 -101
- package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +333 -360
- package/pipeline/skills/shared/external/apple-on-device-ai/evals/evals.json +24 -27
- package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-conversion.md +215 -322
- package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-optimization.md +161 -256
- package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models.md +277 -387
- package/pipeline/skills/shared/external/apple-on-device-ai/references/mlx-swift.md +196 -210
- package/pipeline/skills/shared/external/authentication/SKILL.md +265 -381
- package/pipeline/skills/shared/external/authentication/evals/evals.json +25 -25
- package/pipeline/skills/shared/external/authentication/references/keychain-biometric.md +133 -178
- package/pipeline/skills/shared/external/authentication/references/passkeys.md +111 -147
- package/pipeline/skills/shared/external/avkit/SKILL.md +267 -364
- package/pipeline/skills/shared/external/avkit/evals/evals.json +26 -26
- package/pipeline/skills/shared/external/avkit/references/avkit-patterns.md +375 -493
- package/pipeline/skills/shared/external/background-processing/SKILL.md +270 -382
- package/pipeline/skills/shared/external/background-processing/evals/evals.json +22 -22
- package/pipeline/skills/shared/external/background-processing/references/background-task-patterns.md +169 -317
- package/pipeline/skills/shared/external/callkit-voip/SKILL.md +290 -371
- package/pipeline/skills/shared/external/callkit-voip/evals/evals.json +24 -24
- package/pipeline/skills/shared/external/callkit-voip/references/callkit-patterns.md +175 -343
- package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +292 -381
- package/pipeline/skills/shared/external/cloudkit-sync/evals/evals.json +33 -30
- package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-patterns.md +227 -355
- package/pipeline/skills/shared/external/contacts-framework/SKILL.md +197 -346
- package/pipeline/skills/shared/external/contacts-framework/evals/evals.json +19 -21
- package/pipeline/skills/shared/external/contacts-framework/references/contacts-patterns.md +169 -308
- package/pipeline/skills/shared/external/core-bluetooth/SKILL.md +226 -376
- package/pipeline/skills/shared/external/core-bluetooth/evals/evals.json +25 -22
- package/pipeline/skills/shared/external/core-bluetooth/references/ble-patterns.md +257 -337
- package/pipeline/skills/shared/external/core-data/SKILL.md +292 -368
- package/pipeline/skills/shared/external/core-data/evals/evals.json +30 -27
- package/pipeline/skills/shared/external/core-motion/SKILL.md +235 -324
- package/pipeline/skills/shared/external/core-motion/evals/evals.json +31 -27
- package/pipeline/skills/shared/external/core-motion/references/motion-patterns.md +210 -310
- package/pipeline/skills/shared/external/core-nfc/SKILL.md +292 -366
- package/pipeline/skills/shared/external/core-nfc/evals/evals.json +22 -24
- package/pipeline/skills/shared/external/core-nfc/references/nfc-patterns.md +233 -329
- package/pipeline/skills/shared/external/coreml/SKILL.md +323 -367
- package/pipeline/skills/shared/external/coreml/evals/evals.json +24 -21
- package/pipeline/skills/shared/external/coreml/references/coreml-swift-integration.md +562 -565
- package/pipeline/skills/shared/external/cryptokit/SKILL.md +253 -394
- package/pipeline/skills/shared/external/cryptokit/evals/evals.json +20 -18
- package/pipeline/skills/shared/external/cryptokit/references/cryptokit-patterns.md +299 -488
- package/pipeline/skills/shared/external/debugging-instruments/SKILL.md +270 -323
- package/pipeline/skills/shared/external/debugging-instruments/evals/evals.json +27 -30
- package/pipeline/skills/shared/external/debugging-instruments/references/instruments-guide.md +167 -315
- package/pipeline/skills/shared/external/debugging-instruments/references/lldb-patterns.md +140 -193
- package/pipeline/skills/shared/external/device-integrity/SKILL.md +230 -353
- package/pipeline/skills/shared/external/device-integrity/evals/evals.json +25 -21
- package/pipeline/skills/shared/external/device-integrity/references/device-integrity-patterns.md +159 -197
- package/pipeline/skills/shared/external/energykit/SKILL.md +225 -392
- package/pipeline/skills/shared/external/energykit/evals/evals.json +29 -28
- package/pipeline/skills/shared/external/energykit/references/energykit-patterns.md +174 -470
- package/pipeline/skills/shared/external/eventkit-calendar/SKILL.md +261 -383
- package/pipeline/skills/shared/external/eventkit-calendar/evals/evals.json +25 -22
- package/pipeline/skills/shared/external/eventkit-calendar/references/eventkit-patterns.md +165 -268
- package/pipeline/skills/shared/external/healthkit/SKILL.md +252 -303
- package/pipeline/skills/shared/external/healthkit/evals/evals.json +24 -23
- package/pipeline/skills/shared/external/healthkit/references/healthkit-patterns.md +369 -523
- package/pipeline/skills/shared/external/homekit-matter/SKILL.md +233 -348
- package/pipeline/skills/shared/external/homekit-matter/evals/evals.json +27 -22
- package/pipeline/skills/shared/external/homekit-matter/references/matter-commissioning.md +199 -305
- package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +368 -340
- package/pipeline/skills/shared/external/ios-accessibility/evals/evals.json +28 -27
- package/pipeline/skills/shared/external/ios-accessibility/references/a11y-patterns.md +314 -260
- package/pipeline/skills/shared/external/ios-accessibility/references/media-accessibility.md +97 -67
- package/pipeline/skills/shared/external/ios-accessibility/references/nutrition-labels.md +165 -101
- package/pipeline/skills/shared/external/ios-localization/SKILL.md +258 -371
- package/pipeline/skills/shared/external/ios-localization/evals/evals.json +23 -23
- package/pipeline/skills/shared/external/ios-localization/references/formatstyle-locale.md +283 -491
- package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +313 -440
- package/pipeline/skills/shared/external/ios-networking/SKILL.md +265 -341
- package/pipeline/skills/shared/external/ios-networking/evals/evals.json +24 -24
- package/pipeline/skills/shared/external/ios-networking/references/background-websocket.md +425 -652
- package/pipeline/skills/shared/external/ios-networking/references/file-storage-patterns.md +143 -285
- package/pipeline/skills/shared/external/ios-networking/references/lightweight-clients.md +93 -53
- package/pipeline/skills/shared/external/ios-networking/references/network-framework.md +231 -456
- package/pipeline/skills/shared/external/ios-networking/references/urlsession-patterns.md +517 -784
- package/pipeline/skills/shared/external/ios-simulator/SKILL.md +265 -393
- package/pipeline/skills/shared/external/ios-simulator/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/ios-simulator/references/simctl-commands.md +177 -270
- package/pipeline/skills/shared/external/live-activities/SKILL.md +318 -360
- package/pipeline/skills/shared/external/live-activities/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/live-activities/references/activitykit-patterns.md +478 -710
- package/pipeline/skills/shared/external/mapkit-location/SKILL.md +295 -267
- package/pipeline/skills/shared/external/mapkit-location/evals/evals.json +28 -24
- package/pipeline/skills/shared/external/mapkit-location/references/mapkit-corelocation-patterns.md +378 -532
- package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +397 -499
- package/pipeline/skills/shared/external/metrickit-diagnostics/SKILL.md +165 -348
- package/pipeline/skills/shared/external/metrickit-diagnostics/evals/evals.json +26 -23
- package/pipeline/skills/shared/external/metrickit-diagnostics/references/metrickit-patterns.md +123 -130
- package/pipeline/skills/shared/external/musickit-audio/SKILL.md +189 -315
- package/pipeline/skills/shared/external/musickit-audio/evals/evals.json +22 -21
- package/pipeline/skills/shared/external/musickit-audio/references/musickit-patterns.md +181 -270
- package/pipeline/skills/shared/external/natural-language/SKILL.md +188 -340
- package/pipeline/skills/shared/external/natural-language/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/natural-language/references/translation-patterns.md +171 -225
- package/pipeline/skills/shared/external/passkit-wallet/SKILL.md +258 -392
- package/pipeline/skills/shared/external/passkit-wallet/evals/evals.json +30 -29
- package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +164 -231
- package/pipeline/skills/shared/external/pdfkit/SKILL.md +312 -344
- package/pipeline/skills/shared/external/pdfkit/evals/evals.json +19 -19
- package/pipeline/skills/shared/external/pdfkit/references/pdfkit-patterns.md +413 -624
- package/pipeline/skills/shared/external/pencilkit-drawing/SKILL.md +242 -358
- package/pipeline/skills/shared/external/pencilkit-drawing/evals/evals.json +25 -21
- package/pipeline/skills/shared/external/pencilkit-drawing/references/pencilkit-patterns.md +161 -226
- package/pipeline/skills/shared/external/permissionkit/SKILL.md +282 -400
- package/pipeline/skills/shared/external/permissionkit/evals/evals.json +27 -30
- package/pipeline/skills/shared/external/permissionkit/references/permissionkit-patterns.md +237 -350
- package/pipeline/skills/shared/external/photos-camera-media/SKILL.md +276 -325
- package/pipeline/skills/shared/external/photos-camera-media/references/av-playback.md +299 -545
- package/pipeline/skills/shared/external/photos-camera-media/references/camera-capture.md +344 -588
- package/pipeline/skills/shared/external/photos-camera-media/references/image-loading-caching.md +316 -660
- package/pipeline/skills/shared/external/photos-camera-media/references/photokit-patterns.md +270 -416
- package/pipeline/skills/shared/external/push-notifications/SKILL.md +312 -340
- package/pipeline/skills/shared/external/push-notifications/evals/evals.json +27 -26
- package/pipeline/skills/shared/external/push-notifications/references/notification-patterns.md +328 -485
- package/pipeline/skills/shared/external/push-notifications/references/rich-notifications.md +327 -560
- package/pipeline/skills/shared/external/realitykit-ar/SKILL.md +218 -410
- package/pipeline/skills/shared/external/realitykit-ar/evals/evals.json +24 -27
- package/pipeline/skills/shared/external/realitykit-ar/references/realitykit-patterns.md +221 -348
- package/pipeline/skills/shared/external/shareplay-activities/SKILL.md +222 -393
- package/pipeline/skills/shared/external/shareplay-activities/evals/evals.json +23 -24
- package/pipeline/skills/shared/external/shareplay-activities/references/shareplay-patterns.md +280 -420
- package/pipeline/skills/shared/external/speech-recognition/SKILL.md +217 -421
- package/pipeline/skills/shared/external/speech-recognition/evals/evals.json +23 -26
- package/pipeline/skills/shared/external/speech-recognition/references/speechanalyzer-patterns.md +133 -125
- package/pipeline/skills/shared/external/storekit/SKILL.md +228 -204
- package/pipeline/skills/shared/external/storekit/evals/evals.json +27 -24
- package/pipeline/skills/shared/external/storekit/references/app-review-guidelines.md +98 -109
- package/pipeline/skills/shared/external/storekit/references/core-patterns.md +298 -242
- package/pipeline/skills/shared/external/storekit/references/storekit-advanced.md +356 -649
- package/pipeline/skills/shared/external/swift-api-design-guidelines/SKILL.md +274 -399
- package/pipeline/skills/shared/external/swift-api-design-guidelines/evals/evals.json +22 -24
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/argument-labels-and-parameters.md +107 -108
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/conventions-and-special-rules.md +93 -165
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/naming-and-clarity.md +99 -137
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/side-effects-and-mutating-pairs.md +77 -120
- package/pipeline/skills/shared/external/swift-architecture/SKILL.md +334 -350
- package/pipeline/skills/shared/external/swift-architecture/evals/evals.json +22 -22
- package/pipeline/skills/shared/external/swift-charts/SKILL.md +208 -394
- package/pipeline/skills/shared/external/swift-charts/evals/evals.json +27 -30
- package/pipeline/skills/shared/external/swift-charts/references/charts-patterns.md +351 -762
- package/pipeline/skills/shared/external/swift-codable/SKILL.md +339 -343
- package/pipeline/skills/shared/external/swift-codable/evals/evals.json +20 -20
- package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +303 -351
- package/pipeline/skills/shared/external/swift-concurrency/evals/evals.json +27 -24
- package/pipeline/skills/shared/external/swift-concurrency/references/approachable-concurrency.md +65 -80
- package/pipeline/skills/shared/external/swift-concurrency/references/async-algorithms.md +48 -84
- package/pipeline/skills/shared/external/swift-concurrency/references/bridging-interop.md +134 -79
- package/pipeline/skills/shared/external/swift-concurrency/references/concurrency-patterns.md +145 -167
- package/pipeline/skills/shared/external/swift-concurrency/references/diagnostics.md +62 -50
- package/pipeline/skills/shared/external/swift-concurrency/references/swiftui-concurrency.md +92 -121
- package/pipeline/skills/shared/external/swift-concurrency/references/synchronization-primitives.md +177 -241
- package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +258 -234
- package/pipeline/skills/shared/external/swift-language/SKILL.md +342 -382
- package/pipeline/skills/shared/external/swift-language/evals/evals.json +24 -27
- package/pipeline/skills/shared/external/swift-language/references/swift-attributes-interop.md +79 -56
- package/pipeline/skills/shared/external/swift-language/references/swift-patterns-extended.md +297 -340
- package/pipeline/skills/shared/external/swift-security/SKILL.md +180 -161
- package/pipeline/skills/shared/external/swift-security/evals/evals.json +25 -25
- package/pipeline/skills/shared/external/swift-security/references/biometric-authentication.md +314 -469
- package/pipeline/skills/shared/external/swift-security/references/certificate-trust.md +408 -476
- package/pipeline/skills/shared/external/swift-security/references/common-anti-patterns.md +260 -530
- package/pipeline/skills/shared/external/swift-security/references/compliance-owasp-mapping.md +270 -477
- package/pipeline/skills/shared/external/swift-security/references/credential-storage-patterns.md +573 -571
- package/pipeline/skills/shared/external/swift-security/references/cryptokit-public-key.md +370 -441
- package/pipeline/skills/shared/external/swift-security/references/cryptokit-symmetric.md +332 -433
- package/pipeline/skills/shared/external/swift-security/references/keychain-access-control.md +346 -468
- package/pipeline/skills/shared/external/swift-security/references/keychain-fundamentals.md +352 -472
- package/pipeline/skills/shared/external/swift-security/references/keychain-item-classes.md +431 -432
- package/pipeline/skills/shared/external/swift-security/references/keychain-sharing.md +328 -425
- package/pipeline/skills/shared/external/swift-security/references/migration-legacy-stores.md +341 -579
- package/pipeline/skills/shared/external/swift-security/references/secure-enclave.md +396 -457
- package/pipeline/skills/shared/external/swift-security/references/testing-security-code.md +354 -614
- package/pipeline/skills/shared/external/swift-testing/SKILL.md +188 -175
- package/pipeline/skills/shared/external/swift-testing/evals/evals.json +26 -24
- package/pipeline/skills/shared/external/swift-testing/references/testing-advanced.md +80 -84
- package/pipeline/skills/shared/external/swift-testing/references/testing-patterns.md +317 -433
- package/pipeline/skills/shared/external/swiftdata/SKILL.md +392 -256
- package/pipeline/skills/shared/external/swiftdata/evals/evals.json +24 -24
- package/pipeline/skills/shared/external/swiftdata/references/core-data-coexistence.md +206 -402
- package/pipeline/skills/shared/external/swiftdata/references/indexing.md +59 -52
- package/pipeline/skills/shared/external/swiftdata/references/predicate-pitfalls.md +57 -33
- package/pipeline/skills/shared/external/swiftdata/references/swiftdata-advanced.md +354 -747
- package/pipeline/skills/shared/external/swiftdata/references/swiftdata-queries.md +300 -508
- package/pipeline/skills/shared/external/swiftlint/SKILL.md +175 -226
- package/pipeline/skills/shared/external/swiftlint/references/adoption-and-configuration.md +141 -208
- package/pipeline/skills/shared/external/swiftlint/references/custom-rules-and-analyze.md +100 -109
- package/pipeline/skills/shared/external/swiftlint/references/plugins-run-scripts-and-integrations.md +159 -179
- package/pipeline/skills/shared/external/swiftlint/references/rule-reference.md +383 -18
- package/pipeline/skills/shared/external/swiftlint/references/rules-suppressions-and-baselines.md +143 -229
- package/pipeline/skills/shared/external/swiftui-animation/SKILL.md +283 -366
- package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +396 -608
- package/pipeline/skills/shared/external/swiftui-animation/references/core-animation-bridge.md +336 -385
- package/pipeline/skills/shared/external/swiftui-gestures/SKILL.md +239 -349
- package/pipeline/skills/shared/external/swiftui-gestures/references/gesture-patterns.md +228 -310
- package/pipeline/skills/shared/external/swiftui-layout-components/SKILL.md +260 -249
- package/pipeline/skills/shared/external/swiftui-layout-components/references/form.md +92 -74
- package/pipeline/skills/shared/external/swiftui-layout-components/references/grids.md +112 -177
- package/pipeline/skills/shared/external/swiftui-layout-components/references/list.md +61 -64
- package/pipeline/skills/shared/external/swiftui-layout-components/references/scrollview.md +94 -134
- package/pipeline/skills/shared/external/swiftui-liquid-glass/SKILL.md +193 -225
- package/pipeline/skills/shared/external/swiftui-liquid-glass/references/liquid-glass.md +173 -327
- package/pipeline/skills/shared/external/swiftui-navigation/SKILL.md +193 -168
- package/pipeline/skills/shared/external/swiftui-navigation/references/deeplinks.md +127 -150
- package/pipeline/skills/shared/external/swiftui-navigation/references/navigationstack.md +132 -133
- package/pipeline/skills/shared/external/swiftui-navigation/references/sheets.md +152 -117
- package/pipeline/skills/shared/external/swiftui-navigation/references/tabview.md +106 -140
- package/pipeline/skills/shared/external/swiftui-patterns/SKILL.md +316 -252
- package/pipeline/skills/shared/external/swiftui-patterns/references/architecture-patterns.md +341 -332
- package/pipeline/skills/shared/external/swiftui-patterns/references/deprecated-migration.md +547 -854
- package/pipeline/skills/shared/external/swiftui-patterns/references/design-polish.md +485 -537
- package/pipeline/skills/shared/external/swiftui-patterns/references/platform-and-sharing.md +417 -499
- package/pipeline/skills/shared/external/swiftui-performance/SKILL.md +213 -376
- package/pipeline/skills/shared/external/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +86 -175
- package/pipeline/skills/shared/external/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +89 -195
- package/pipeline/skills/shared/external/swiftui-performance/references/understanding-hangs-in-your-app.md +95 -182
- package/pipeline/skills/shared/external/swiftui-performance/references/understanding-improving-swiftui-performance.md +71 -149
- package/pipeline/skills/shared/external/swiftui-performance/references/wwdc-session-sources.md +21 -27
- package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +303 -295
- package/pipeline/skills/shared/external/swiftui-uikit-interop/references/hosting-migration.md +204 -387
- package/pipeline/skills/shared/external/swiftui-uikit-interop/references/representable-recipes.md +469 -683
- package/pipeline/skills/shared/external/swiftui-webkit/SKILL.md +140 -186
- package/pipeline/skills/shared/external/swiftui-webkit/references/loading-and-observation.md +75 -86
- package/pipeline/skills/shared/external/swiftui-webkit/references/local-content-and-custom-schemes.md +63 -60
- package/pipeline/skills/shared/external/swiftui-webkit/references/migration-and-fallbacks.md +69 -137
- package/pipeline/skills/shared/external/swiftui-webkit/references/navigation-and-javascript.md +95 -67
- package/pipeline/skills/shared/external/tipkit/SKILL.md +220 -335
- package/pipeline/skills/shared/external/tipkit/references/tipkit-patterns.md +356 -494
- package/pipeline/skills/shared/external/vision-framework/SKILL.md +260 -375
- package/pipeline/skills/shared/external/vision-framework/references/vision-requests.md +393 -515
- package/pipeline/skills/shared/external/vision-framework/references/visionkit-scanner.md +363 -539
- package/pipeline/skills/shared/external/weatherkit/SKILL.md +152 -310
- package/pipeline/skills/shared/external/weatherkit/references/weatherkit-patterns.md +288 -407
- package/pipeline/skills/shared/external/widgetkit/SKILL.md +216 -288
- package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +414 -719
- package/pipeline/skills/shared/external/NOTICE-swift-ios-skills.md +0 -39
|
@@ -1,483 +1,367 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: pencilkit-drawing
|
|
3
|
-
description: "
|
|
3
|
+
description: "PencilKit drawing with Apple Pencil or finger: PKCanvasView, PKToolPicker and custom items, PKDrawing persistence and export, stroke inspection and construction, content-version safe ink, SwiftUI wrapping, PaperKit handoffs. Use for drawing apps, annotation, signatures or handwriting capture on iOS, iPadOS or visionOS."
|
|
4
4
|
metadata:
|
|
5
|
-
source:
|
|
5
|
+
source: multi-agent-pipeline
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# PencilKit
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
- [Setup](#setup)
|
|
17
|
-
- [PKCanvasView Basics](#pkcanvasview-basics)
|
|
18
|
-
- [PKToolPicker](#pktoolpicker)
|
|
19
|
-
- [PKDrawing Serialization](#pkdrawing-serialization)
|
|
20
|
-
- [Content Version Compatibility](#content-version-compatibility)
|
|
21
|
-
- [Exporting to Image](#exporting-to-image)
|
|
22
|
-
- [Stroke Inspection](#stroke-inspection)
|
|
23
|
-
- [SwiftUI Integration](#swiftui-integration)
|
|
24
|
-
- [PaperKit Relationship](#paperkit-relationship)
|
|
25
|
-
- [Common Mistakes](#common-mistakes)
|
|
26
|
-
- [Review Checklist](#review-checklist)
|
|
27
|
-
- [References](#references)
|
|
10
|
+
PencilKit gives you three pieces: `PKCanvasView` turns Pencil and touch input
|
|
11
|
+
into strokes, `PKToolPicker` is the floating palette that picks the tool, and
|
|
12
|
+
`PKDrawing` is the value type you save, load and render. The code assumes
|
|
13
|
+
Swift 6.3 and an iOS 26 deployment target unless an availability note says
|
|
14
|
+
otherwise.
|
|
28
15
|
|
|
29
16
|
## Setup
|
|
30
17
|
|
|
31
|
-
|
|
32
|
-
and
|
|
18
|
+
Nothing to configure: no entitlement, no Info.plist key. `import PencilKit`
|
|
19
|
+
is enough. The framework exists from iOS and iPadOS 13, from Mac Catalyst
|
|
20
|
+
13.1 and from visionOS 1.0.
|
|
33
21
|
|
|
34
|
-
|
|
35
|
-
import PencilKit
|
|
36
|
-
```
|
|
22
|
+
## The canvas
|
|
37
23
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
## PKCanvasView Basics
|
|
41
|
-
|
|
42
|
-
`PKCanvasView` is a `UIScrollView` subclass that captures Apple Pencil and
|
|
43
|
-
finger input and renders strokes.
|
|
24
|
+
`PKCanvasView` inherits from `UIScrollView`. It records input, renders the
|
|
25
|
+
strokes and reports every edit to its delegate.
|
|
44
26
|
|
|
45
27
|
```swift
|
|
46
28
|
import PencilKit
|
|
47
29
|
import UIKit
|
|
48
30
|
|
|
49
|
-
class
|
|
50
|
-
let
|
|
31
|
+
final class SketchViewController: UIViewController, PKCanvasViewDelegate {
|
|
32
|
+
private let board = PKCanvasView()
|
|
51
33
|
|
|
52
34
|
override func viewDidLoad() {
|
|
53
35
|
super.viewDidLoad()
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
view.addSubview(
|
|
36
|
+
board.delegate = self
|
|
37
|
+
board.drawingPolicy = .anyInput
|
|
38
|
+
board.tool = PKInkingTool(.marker, color: .systemTeal, width: 12)
|
|
39
|
+
board.frame = view.bounds
|
|
40
|
+
board.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
|
41
|
+
view.addSubview(board)
|
|
60
42
|
}
|
|
61
43
|
|
|
62
44
|
func canvasViewDrawingDidChange(_ canvasView: PKCanvasView) {
|
|
63
|
-
//
|
|
45
|
+
scheduleAutosave(canvasView.drawing) // persist or post-process here
|
|
64
46
|
}
|
|
47
|
+
|
|
48
|
+
private func scheduleAutosave(_ drawing: PKDrawing) { }
|
|
65
49
|
}
|
|
66
50
|
```
|
|
67
51
|
|
|
68
|
-
###
|
|
52
|
+
### Who is allowed to draw
|
|
69
53
|
|
|
70
|
-
| Policy |
|
|
54
|
+
| Policy | Behaviour |
|
|
71
55
|
|---|---|
|
|
72
|
-
| `.default` |
|
|
73
|
-
| `.anyInput` |
|
|
74
|
-
| `.pencilOnly` | Only
|
|
56
|
+
| `.default` | While the tool picker is showing, follows the user's `UIPencilInteraction.prefersPencilOnlyDrawing` setting. With no picker, only Pencil draws. |
|
|
57
|
+
| `.anyInput` | Fingers and Pencil both draw. |
|
|
58
|
+
| `.pencilOnly` | Only Pencil touches draw; fingers never make strokes. |
|
|
59
|
+
|
|
60
|
+
Pick `.default` for a Pencil-first canvas that should respect the system
|
|
61
|
+
setting, `.anyInput` for signature boxes, whiteboards or an explicit
|
|
62
|
+
"draw with finger" mode, and `.pencilOnly` when finger touches must stay
|
|
63
|
+
reserved for scrolling or selection. Do not describe `.default` as
|
|
64
|
+
"Pencil only": it tracks the user's preference whenever the picker is up.
|
|
65
|
+
|
|
66
|
+
### Canvas options
|
|
75
67
|
|
|
76
68
|
```swift
|
|
77
|
-
|
|
69
|
+
board.contentSize = CGSize(width: 2400, height: 3600) // large scrollable page
|
|
70
|
+
board.isRulerActive = true
|
|
71
|
+
board.tool = PKInkingTool(.pencil, color: .darkGray, width: 3)
|
|
72
|
+
board.tool = PKEraserTool(.vector)
|
|
78
73
|
```
|
|
79
74
|
|
|
80
|
-
|
|
81
|
-
picker's drawing-policy control should follow the user's Pencil preference. Use
|
|
82
|
-
`.anyInput` for signature pads, whiteboards, or explicit finger-drawing modes.
|
|
83
|
-
Use `.pencilOnly` when finger input should never create strokes.
|
|
75
|
+
## Tool picker
|
|
84
76
|
|
|
85
|
-
|
|
77
|
+
`PKToolPicker` floats over the content. Every canvas registered as an
|
|
78
|
+
observer adopts whatever tool the user selects. Keep one picker per canvas as
|
|
79
|
+
a stored property, and show it once the view is on screen:
|
|
86
80
|
|
|
87
81
|
```swift
|
|
88
|
-
|
|
89
|
-
canvasView.contentSize = CGSize(width: 2000, height: 3000)
|
|
90
|
-
|
|
91
|
-
// Enable/disable the ruler
|
|
92
|
-
canvasView.isRulerActive = true
|
|
82
|
+
private let palette = PKToolPicker()
|
|
93
83
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
84
|
+
override func viewDidAppear(_ isAnimated: Bool) {
|
|
85
|
+
super.viewDidAppear(isAnimated)
|
|
86
|
+
palette.addObserver(board)
|
|
87
|
+
palette.setVisible(true, forFirstResponder: board)
|
|
88
|
+
board.becomeFirstResponder()
|
|
89
|
+
}
|
|
97
90
|
```
|
|
98
91
|
|
|
99
|
-
|
|
92
|
+
### Curated palettes
|
|
100
93
|
|
|
101
|
-
`PKToolPicker`
|
|
102
|
-
|
|
94
|
+
`PKToolPicker(toolItems:)` and the item classes need iOS or iPadOS 18,
|
|
95
|
+
visionOS 2, or Mac Catalyst 18; on macOS the item classes arrive with
|
|
96
|
+
macOS 26. `PKToolPickerRulerItem` is the exception: visionOS has no ruler
|
|
97
|
+
item, so add it only on the other platforms.
|
|
103
98
|
|
|
104
99
|
```swift
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
100
|
+
if #available(iOS 18.0, visionOS 2.0, *) {
|
|
101
|
+
var items: [PKToolPickerItem] = [
|
|
102
|
+
PKToolPickerInkingItem(type: .fountainPen, color: .black, width: 4),
|
|
103
|
+
PKToolPickerEraserItem(type: .vector),
|
|
104
|
+
PKToolPickerLassoItem()
|
|
105
|
+
]
|
|
106
|
+
#if !os(visionOS)
|
|
107
|
+
items.append(PKToolPickerRulerItem())
|
|
108
|
+
#endif
|
|
109
|
+
let limited = PKToolPicker(toolItems: items)
|
|
110
|
+
limited.addObserver(board)
|
|
115
111
|
}
|
|
116
112
|
```
|
|
117
113
|
|
|
118
|
-
###
|
|
114
|
+
### Ink catalogue
|
|
119
115
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
116
|
+
| Ink | Look |
|
|
117
|
+
|---|---|
|
|
118
|
+
| `.pen` | Smooth line that responds to pressure |
|
|
119
|
+
| `.pencil` | Grainy line with tilt shading |
|
|
120
|
+
| `.marker` | Translucent highlighter |
|
|
121
|
+
| `.monoline` | Constant width |
|
|
122
|
+
| `.fountainPen` | Width varies like a calligraphy nib |
|
|
123
|
+
| `.watercolor` | Colours blend into each other |
|
|
124
|
+
| `.crayon` | Waxy texture |
|
|
125
|
+
| `.reed` | Reed nib; version 26 of iOS, iPadOS, macOS and visionOS |
|
|
123
126
|
|
|
124
|
-
|
|
125
|
-
let toolPicker = PKToolPicker(toolItems: [
|
|
126
|
-
PKToolPickerInkingItem(type: .pen, color: .black, width: 5),
|
|
127
|
-
PKToolPickerInkingItem(type: .pencil, color: .gray, width: 5),
|
|
128
|
-
PKToolPickerInkingItem(type: .marker, color: .yellow, width: 12),
|
|
129
|
-
PKToolPickerEraserItem(type: .vector),
|
|
130
|
-
PKToolPickerLassoItem(),
|
|
131
|
-
PKToolPickerRulerItem()
|
|
132
|
-
])
|
|
133
|
-
```
|
|
127
|
+
## Content versions
|
|
134
128
|
|
|
135
|
-
|
|
129
|
+
Newer inks and stroke data cannot be read by older systems: loading such a
|
|
130
|
+
drawing throws there. `PKContentVersion` names each step:
|
|
136
131
|
|
|
137
|
-
|
|
|
132
|
+
| Version | What it adds |
|
|
138
133
|
|---|---|
|
|
139
|
-
| `.
|
|
140
|
-
| `.
|
|
141
|
-
| `.
|
|
142
|
-
| `.monoline` | Uniform-width pen |
|
|
143
|
-
| `.fountainPen` | Variable-width calligraphy pen |
|
|
144
|
-
| `.watercolor` | Blendable watercolor brush |
|
|
145
|
-
| `.crayon` | Textured crayon |
|
|
146
|
-
| `.reed` | Reed pen (iOS/iPadOS/macOS/visionOS 26+) |
|
|
147
|
-
|
|
148
|
-
### Content Versions
|
|
149
|
-
|
|
150
|
-
When drawings sync to older OS versions, check `requiredContentVersion` before
|
|
151
|
-
uploading or cap new content by setting `maximumSupportedContentVersion` on
|
|
152
|
-
both the `PKCanvasView` and `PKToolPicker`.
|
|
153
|
-
|
|
154
|
-
| Version | Content |
|
|
155
|
-
|---|---|
|
|
156
|
-
| `.version1` | iPadOS 14-era inks: marker, pen, pencil |
|
|
157
|
-
| `.version2` | iPadOS 17 inks: monoline, fountain pen, watercolor, crayon |
|
|
158
|
-
| `.version3` | Barrel-roll angle data |
|
|
134
|
+
| `.version1` | Original inks: pen, pencil, marker (iPadOS 14 era) |
|
|
135
|
+
| `.version2` | Inks added in iPadOS 17: `.monoline`, `.fountainPen`, `.watercolor`, `.crayon` |
|
|
136
|
+
| `.version3` | Barrel-roll angle data on strokes |
|
|
159
137
|
| `.version4` | Reed pen |
|
|
160
138
|
|
|
161
|
-
|
|
162
|
-
cap
|
|
163
|
-
|
|
164
|
-
When existing content exceeds the target OS version, sync a verified fallback
|
|
165
|
-
`PKDrawing` or restrict editing up front; do not rely only on a warning.
|
|
139
|
+
When reviewing compatibility, lay out this whole table before recommending a
|
|
140
|
+
cap, and if the fix is a curated picker, say that `PKToolPicker(toolItems:)`
|
|
141
|
+
needs iOS 18 or visionOS 2.
|
|
166
142
|
|
|
167
|
-
|
|
143
|
+
### The gate
|
|
168
144
|
|
|
169
|
-
`
|
|
170
|
-
|
|
145
|
+
`drawing.requiredContentVersion` is what decides whether a drawing may go to
|
|
146
|
+
a given device, whether through sync, a migration, a downgrade or another
|
|
147
|
+
device. When
|
|
148
|
+
older clients must keep editing, cap new input by setting
|
|
149
|
+
`maximumSupportedContentVersion` on the picker **and** on the canvas;
|
|
150
|
+
capping only the canvas still lets the picker hand out newer inks.
|
|
151
|
+
`PKContentVersion` and both properties are iOS 17 API; an app that still
|
|
152
|
+
deploys below 17 wraps this whole block in `if #available(iOS 17.0, *)`.
|
|
171
153
|
|
|
172
154
|
```swift
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
try data.write(to: fileURL)
|
|
177
|
-
}
|
|
155
|
+
let oldestClient: PKContentVersion = .version1
|
|
156
|
+
board.maximumSupportedContentVersion = oldestClient
|
|
157
|
+
palette.maximumSupportedContentVersion = oldestClient
|
|
178
158
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
159
|
+
switch sketch.requiredContentVersion {
|
|
160
|
+
case .version1:
|
|
161
|
+
queueForSync(sketch) // everyone can open and edit it
|
|
162
|
+
case .version2:
|
|
163
|
+
if recipientsHandleVersion2 { queueForSync(sketch) }
|
|
164
|
+
case .version3, .version4:
|
|
165
|
+
keepForCurrentClientsOnly(sketch)
|
|
166
|
+
@unknown default:
|
|
167
|
+
showReadOnlyPreview(sketch)
|
|
183
168
|
}
|
|
184
169
|
```
|
|
185
170
|
|
|
186
|
-
|
|
187
|
-
|
|
171
|
+
If a recipient cannot load the version, keep the full drawing for clients
|
|
172
|
+
that can, and give the others a read-only preview or a separately built
|
|
173
|
+
fallback. Never overwrite the original silently, and do not stop at showing a
|
|
174
|
+
warning: either sync a checked fallback `PKDrawing` or restrict editing before
|
|
175
|
+
the user draws. A fallback builder is in
|
|
176
|
+
[pencilkit patterns](references/pencilkit-patterns.md).
|
|
188
177
|
|
|
189
|
-
|
|
190
|
-
do {
|
|
191
|
-
canvasView.drawing = try PKDrawing(data: data)
|
|
192
|
-
} catch {
|
|
193
|
-
showReadOnlyPreview(for: document, loadError: error)
|
|
194
|
-
}
|
|
195
|
-
```
|
|
178
|
+
## Saving and loading
|
|
196
179
|
|
|
197
|
-
|
|
180
|
+
`PKDrawing` is a struct that owns every stroke.
|
|
198
181
|
|
|
199
182
|
```swift
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
183
|
+
func store(_ sketch: PKDrawing, at url: URL) throws {
|
|
184
|
+
try sketch.dataRepresentation().write(to: url, options: .atomic)
|
|
185
|
+
}
|
|
203
186
|
|
|
204
|
-
|
|
205
|
-
|
|
187
|
+
func restore(from url: URL) throws -> PKDrawing {
|
|
188
|
+
try PKDrawing(data: Data(contentsOf: url))
|
|
189
|
+
}
|
|
206
190
|
```
|
|
207
191
|
|
|
208
|
-
|
|
192
|
+
For data that came from sync or from the user, catch the decoding error and
|
|
193
|
+
react to it (for example switch to a read-only preview) instead of hiding it
|
|
194
|
+
behind `try?`.
|
|
209
195
|
|
|
210
196
|
```swift
|
|
211
|
-
|
|
212
|
-
|
|
197
|
+
var page = firstLayer
|
|
198
|
+
page.append(secondLayer) // mutates page
|
|
199
|
+
let merged = firstLayer.appending(secondLayer) // returns a new drawing
|
|
200
|
+
let shrunk = merged.transformed(using: CGAffineTransform(scaleX: 0.75, y: 0.75)
|
|
201
|
+
.translatedBy(x: 40, y: 0))
|
|
213
202
|
```
|
|
214
203
|
|
|
215
|
-
##
|
|
216
|
-
|
|
217
|
-
For sync, migration, downgrade, or cross-device editing tasks, use
|
|
218
|
-
`requiredContentVersion` as the compatibility gate and choose an explicit
|
|
219
|
-
`maximumSupportedContentVersion` when old clients must keep editing.
|
|
204
|
+
## Rendering to an image
|
|
220
205
|
|
|
221
206
|
```swift
|
|
222
|
-
let targetVersion: PKContentVersion = .version1
|
|
223
|
-
canvasView.maximumSupportedContentVersion = targetVersion
|
|
224
|
-
toolPicker.maximumSupportedContentVersion = targetVersion
|
|
225
|
-
|
|
226
|
-
switch drawing.requiredContentVersion {
|
|
227
|
-
case .version1:
|
|
228
|
-
// Older marker, pen, and pencil ink set
|
|
229
|
-
syncEditable(drawing)
|
|
230
|
-
case .version2:
|
|
231
|
-
// iPadOS 17-era inks: monoline, fountain pen, watercolor, crayon
|
|
232
|
-
syncIfRecipientsSupportVersion2(drawing)
|
|
233
|
-
case .version3, .version4:
|
|
234
|
-
// Later features such as barrel-roll data and Reed Pen
|
|
235
|
-
syncEditableOnlyToCurrentClients(drawing)
|
|
236
|
-
@unknown default:
|
|
237
|
-
showReadOnlyPreview(for: drawing)
|
|
238
|
-
}
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
If a drawing requires a newer version than a recipient can load, preserve the
|
|
242
|
-
full-fidelity `PKDrawing` for capable clients and provide a read-only preview or
|
|
243
|
-
separate fallback instead of silently overwriting it. See
|
|
244
|
-
[references/pencilkit-patterns.md](references/pencilkit-patterns.md) for the
|
|
245
|
-
deeper compatibility table.
|
|
246
|
-
|
|
247
|
-
## Exporting to Image
|
|
248
|
-
|
|
249
|
-
Generate a `UIImage` from a drawing.
|
|
250
|
-
|
|
251
|
-
```swift
|
|
252
|
-
func exportImage(from drawing: PKDrawing, scale: CGFloat = 2.0) -> UIImage {
|
|
253
|
-
drawing.image(from: drawing.bounds, scale: scale)
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// Export a specific region
|
|
257
|
-
let region = CGRect(x: 0, y: 0, width: 500, height: 500)
|
|
258
207
|
let scale = UITraitCollection.current.displayScale
|
|
259
|
-
|
|
208
|
+
guard !sketch.bounds.isEmpty else { return nil } // empty drawings report .zero
|
|
209
|
+
let full = sketch.image(from: sketch.bounds, scale: scale)
|
|
210
|
+
let corner = sketch.image(from: CGRect(origin: .zero, size: CGSize(width: 300, height: 300)), scale: 2.0)
|
|
260
211
|
```
|
|
261
212
|
|
|
262
|
-
##
|
|
263
|
-
|
|
264
|
-
Access individual strokes, their ink, and control points.
|
|
213
|
+
## Looking inside strokes
|
|
265
214
|
|
|
266
215
|
```swift
|
|
267
|
-
for
|
|
268
|
-
let ink =
|
|
269
|
-
print(
|
|
270
|
-
print("
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
print("Points: \(path.count), created: \(path.creationDate)")
|
|
275
|
-
|
|
276
|
-
// Interpolate along the path
|
|
277
|
-
for point in path.interpolatedPoints(by: .distance(10)) {
|
|
278
|
-
print("Location: \(point.location), force: \(point.force)")
|
|
216
|
+
for mark in sketch.strokes {
|
|
217
|
+
let ink = mark.ink
|
|
218
|
+
print(ink.inkType, ink.color, mark.renderBounds)
|
|
219
|
+
print(mark.path.count, "points, started", mark.path.creationDate)
|
|
220
|
+
|
|
221
|
+
for sample in mark.path.interpolatedPoints(by: .distance(8)) {
|
|
222
|
+
print(sample.location, sample.force)
|
|
279
223
|
}
|
|
280
224
|
}
|
|
281
225
|
```
|
|
282
226
|
|
|
283
|
-
###
|
|
227
|
+
### Building strokes in code
|
|
284
228
|
|
|
285
229
|
```swift
|
|
286
|
-
let
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
let
|
|
230
|
+
let samples = [CGPoint(x: 20, y: 20), CGPoint(x: 120, y: 60), CGPoint(x: 220, y: 20)]
|
|
231
|
+
.enumerated()
|
|
232
|
+
.map { index, point in
|
|
233
|
+
PKStrokePoint(location: point,
|
|
234
|
+
timeOffset: TimeInterval(index) * 0.05,
|
|
235
|
+
size: CGSize(width: 6, height: 6),
|
|
236
|
+
opacity: 1,
|
|
237
|
+
force: 1,
|
|
238
|
+
azimuth: 0,
|
|
239
|
+
altitude: .pi / 2)
|
|
240
|
+
}
|
|
241
|
+
let path = PKStrokePath(controlPoints: samples, creationDate: .now)
|
|
242
|
+
let zigzag = PKStroke(ink: PKInk(.pen, color: .systemIndigo),
|
|
243
|
+
path: path,
|
|
244
|
+
transform: .identity,
|
|
245
|
+
mask: nil)
|
|
246
|
+
let generated = PKDrawing(strokes: [zigzag])
|
|
298
247
|
```
|
|
299
248
|
|
|
300
|
-
## SwiftUI
|
|
249
|
+
## SwiftUI
|
|
301
250
|
|
|
302
|
-
Wrap
|
|
251
|
+
Wrap the canvas in `UIViewRepresentable`. The picker lives in the
|
|
252
|
+
coordinator so it survives view updates, and `updateUIView` only assigns the
|
|
253
|
+
drawing when it actually differs; without that check the delegate and the
|
|
254
|
+
binding feed each other forever.
|
|
303
255
|
|
|
304
256
|
```swift
|
|
305
|
-
import SwiftUI
|
|
306
257
|
import PencilKit
|
|
258
|
+
import SwiftUI
|
|
307
259
|
|
|
308
|
-
struct
|
|
260
|
+
struct InkCanvas: UIViewRepresentable {
|
|
309
261
|
@Binding var drawing: PKDrawing
|
|
310
|
-
@Binding var
|
|
262
|
+
@Binding var paletteShown: Bool
|
|
263
|
+
|
|
264
|
+
func makeCoordinator() -> Coordinator { Coordinator(drawing: $drawing) }
|
|
311
265
|
|
|
312
266
|
func makeUIView(context: Context) -> PKCanvasView {
|
|
313
267
|
let canvas = PKCanvasView()
|
|
314
268
|
canvas.delegate = context.coordinator
|
|
315
269
|
canvas.drawingPolicy = .anyInput
|
|
316
270
|
canvas.drawing = drawing
|
|
317
|
-
context.coordinator.
|
|
271
|
+
context.coordinator.palette.addObserver(canvas)
|
|
318
272
|
return canvas
|
|
319
273
|
}
|
|
320
274
|
|
|
321
|
-
func updateUIView(_
|
|
322
|
-
if
|
|
323
|
-
|
|
275
|
+
func updateUIView(_ uiView: PKCanvasView, context: Context) {
|
|
276
|
+
if uiView.drawing != drawing { // unchanged: skip, or delegate and binding loop
|
|
277
|
+
uiView.drawing = drawing
|
|
324
278
|
}
|
|
325
|
-
let
|
|
326
|
-
|
|
327
|
-
if
|
|
279
|
+
let palette = context.coordinator.palette
|
|
280
|
+
palette.setVisible(paletteShown, forFirstResponder: uiView)
|
|
281
|
+
if paletteShown { uiView.becomeFirstResponder() }
|
|
328
282
|
}
|
|
329
283
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
let parent: CanvasView
|
|
334
|
-
let toolPicker = PKToolPicker()
|
|
284
|
+
final class Coordinator: NSObject, PKCanvasViewDelegate {
|
|
285
|
+
let palette = PKToolPicker()
|
|
286
|
+
private var drawing: Binding<PKDrawing>
|
|
335
287
|
|
|
336
|
-
init(
|
|
337
|
-
self.parent = parent
|
|
338
|
-
super.init()
|
|
339
|
-
}
|
|
288
|
+
init(drawing: Binding<PKDrawing>) { self.drawing = drawing }
|
|
340
289
|
|
|
341
290
|
func canvasViewDrawingDidChange(_ canvasView: PKCanvasView) {
|
|
342
|
-
|
|
291
|
+
drawing.wrappedValue = canvasView.drawing
|
|
343
292
|
}
|
|
344
293
|
}
|
|
345
294
|
}
|
|
346
|
-
```
|
|
347
|
-
|
|
348
|
-
For SwiftUI wrappers, call out the input-policy choice in the wrapper guidance.
|
|
349
|
-
Use `.anyInput` when finger drawing is part of the product. Use `.pencilOnly`
|
|
350
|
-
when touch should stay reserved for scrolling or selection. Use `.default` when
|
|
351
|
-
you want PencilKit's system behavior: with the tool picker visible, it follows
|
|
352
|
-
the user's Pencil-only drawing setting; otherwise only Apple Pencil draws.
|
|
353
295
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
struct DrawingScreen: View {
|
|
358
|
-
@State private var drawing = PKDrawing()
|
|
359
|
-
@State private var showToolPicker = true
|
|
296
|
+
struct NotePage: View {
|
|
297
|
+
@State private var ink = PKDrawing()
|
|
298
|
+
@State private var paletteShown = true
|
|
360
299
|
|
|
361
300
|
var body: some View {
|
|
362
|
-
|
|
301
|
+
InkCanvas(drawing: $ink, paletteShown: $paletteShown)
|
|
363
302
|
.ignoresSafeArea()
|
|
364
303
|
}
|
|
365
304
|
}
|
|
366
305
|
```
|
|
367
306
|
|
|
368
|
-
|
|
307
|
+
Whenever you hand out a wrapper, state which input policy it uses and why:
|
|
308
|
+
`.anyInput` so fingers draw, `.pencilOnly` so touch stays free for scrolling
|
|
309
|
+
and selection, `.default` for system behaviour.
|
|
310
|
+
|
|
311
|
+
## PencilKit and PaperKit
|
|
369
312
|
|
|
370
|
-
PaperKit (iOS 26+)
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
drawing.
|
|
313
|
+
PaperKit (iOS 26+) layers structured markup on top of PencilKit. This skill
|
|
314
|
+
set has no dedicated PaperKit skill, so for markup work use Apple's PaperKit
|
|
315
|
+
documentation and keep this skill for the freeform ink.
|
|
374
316
|
|
|
375
317
|
| Capability | PencilKit | PaperKit |
|
|
376
318
|
|---|---|---|
|
|
377
|
-
| Freeform
|
|
378
|
-
| Shapes
|
|
379
|
-
| Text boxes |
|
|
380
|
-
| Images
|
|
381
|
-
| Loupes |
|
|
382
|
-
| Markup toolbar |
|
|
383
|
-
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
```swift
|
|
416
|
-
// WRONG
|
|
417
|
-
func viewDidAppear(_ animated: Bool) {
|
|
418
|
-
let picker = PKToolPicker() // New picker every appearance
|
|
419
|
-
picker.setVisible(true, forFirstResponder: canvasView)
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
// CORRECT: Store picker as a property
|
|
423
|
-
let toolPicker = PKToolPicker()
|
|
424
|
-
```
|
|
425
|
-
|
|
426
|
-
### DON'T: Ignore content versions for backward compatibility
|
|
427
|
-
|
|
428
|
-
Earlier OS versions throw when loading `PKDrawing` data that uses unsupported
|
|
429
|
-
inks. Check `requiredContentVersion` before syncing, or set
|
|
430
|
-
`maximumSupportedContentVersion` on both the canvas and tool picker to restrict
|
|
431
|
-
new content.
|
|
432
|
-
|
|
433
|
-
```swift
|
|
434
|
-
// WRONG: only limits the canvas; picker can still expose newer inks
|
|
435
|
-
canvasView.tool = PKInkingTool(.watercolor, color: .blue)
|
|
436
|
-
canvasView.maximumSupportedContentVersion = .version1
|
|
437
|
-
|
|
438
|
-
// CORRECT: limit both surfaces for iPadOS 14-era ink compatibility
|
|
439
|
-
if #available(iOS 17.0, *) {
|
|
440
|
-
canvasView.maximumSupportedContentVersion = .version1
|
|
441
|
-
toolPicker.maximumSupportedContentVersion = .version1
|
|
442
|
-
}
|
|
443
|
-
```
|
|
444
|
-
|
|
445
|
-
### DON'T: Compare drawings by data representation
|
|
446
|
-
|
|
447
|
-
`dataRepresentation()` is for persistence and interchange, not comparison.
|
|
448
|
-
Use `PKDrawing` equality for exact value checks, and inspect strokes or rendered
|
|
449
|
-
images for visual/approximate comparisons.
|
|
450
|
-
|
|
451
|
-
```swift
|
|
452
|
-
// WRONG
|
|
453
|
-
if drawing1.dataRepresentation() == drawing2.dataRepresentation() { }
|
|
454
|
-
|
|
455
|
-
// CORRECT
|
|
456
|
-
if drawing1 == drawing2 { }
|
|
457
|
-
```
|
|
458
|
-
|
|
459
|
-
## Review Checklist
|
|
460
|
-
|
|
461
|
-
- [ ] `PKCanvasView.drawingPolicy` set appropriately and `.default` explained as system-setting aware
|
|
462
|
-
- [ ] `PKToolPicker` stored as a property, not recreated each appearance
|
|
463
|
-
- [ ] `canvasView.becomeFirstResponder()` called to show the tool picker
|
|
464
|
-
- [ ] Canvas added as a `PKToolPicker` observer before showing the picker
|
|
465
|
-
- [ ] Drawing serialized via `dataRepresentation()` and loaded via `PKDrawing(data:)`
|
|
466
|
-
- [ ] `canvasViewDrawingDidChange` delegate method used to track changes
|
|
467
|
-
- [ ] `maximumSupportedContentVersion` set on both canvas and tool picker if backward compatibility is needed
|
|
468
|
-
- [ ] Custom tool picker item code guarded for iOS/iPadOS 18+ and visionOS 2+
|
|
469
|
-
- [ ] Exported images use appropriate scale factor for the device
|
|
470
|
-
- [ ] SwiftUI wrapper avoids infinite update loops by checking `drawing != binding`
|
|
471
|
-
- [ ] Drawing bounds checked before image export (empty drawings have `.zero` bounds)
|
|
319
|
+
| Freeform ink | yes | yes |
|
|
320
|
+
| Shapes and lines, arrows | no | yes |
|
|
321
|
+
| Text boxes | no | yes |
|
|
322
|
+
| Images and stickers | no | yes |
|
|
323
|
+
| Loupes | no | yes |
|
|
324
|
+
| Markup toolbar and insertion UI | no | yes: `MarkupEditViewController` on iOS, `MarkupToolbarViewController` on macOS |
|
|
325
|
+
| Model type | `PKDrawing` | `PaperMarkup` |
|
|
326
|
+
|
|
327
|
+
Where they meet: `PaperMarkupViewController.drawingTool` takes any `PKTool`,
|
|
328
|
+
and `PaperMarkup.append(contentsOf:)` accepts a `PKDrawing`.
|
|
329
|
+
|
|
330
|
+
## Common mistakes
|
|
331
|
+
|
|
332
|
+
- **Picker never shows.** It is tied to a responder; call
|
|
333
|
+
`becomeFirstResponder()` on the canvas after `setVisible(_:forFirstResponder:)`.
|
|
334
|
+
- **Calling `.default` Pencil-only.** It follows the system setting while the
|
|
335
|
+
picker is visible.
|
|
336
|
+
- **A new picker on every appearance.** Store one per canvas; extra pickers
|
|
337
|
+
fight over the screen.
|
|
338
|
+
- **Capping only the canvas.** Older systems throw on inks they do not know.
|
|
339
|
+
Set `maximumSupportedContentVersion` on the canvas and on the picker, inside
|
|
340
|
+
`if #available(iOS 17.0, *)`.
|
|
341
|
+
- **Comparing `dataRepresentation()` bytes.** Use `==` on `PKDrawing` for
|
|
342
|
+
exact equality; compare strokes or rendered images for "close enough".
|
|
343
|
+
|
|
344
|
+
## Review checklist
|
|
345
|
+
|
|
346
|
+
- [ ] `drawingPolicy` chosen on purpose; `.default` described as following the system setting
|
|
347
|
+
- [ ] Tool picker kept in a stored property
|
|
348
|
+
- [ ] `becomeFirstResponder()` called on the canvas
|
|
349
|
+
- [ ] Canvas added as picker observer before the picker is shown
|
|
350
|
+
- [ ] Saved with `dataRepresentation()`, loaded with `PKDrawing(data:)`
|
|
351
|
+
- [ ] Edits tracked through `canvasViewDrawingDidChange(_:)`
|
|
352
|
+
- [ ] `maximumSupportedContentVersion` set on canvas and picker when older clients matter
|
|
353
|
+
- [ ] Custom picker items wrapped in an availability check (iOS/iPadOS 18, visionOS 2)
|
|
354
|
+
- [ ] Images rendered at the display scale
|
|
355
|
+
- [ ] SwiftUI wrapper skips assignment when the drawing is unchanged
|
|
356
|
+
- [ ] Bounds checked before export; an empty drawing has `.zero` bounds
|
|
472
357
|
|
|
473
358
|
## References
|
|
474
359
|
|
|
475
|
-
-
|
|
476
|
-
-
|
|
477
|
-
-
|
|
478
|
-
-
|
|
479
|
-
-
|
|
480
|
-
-
|
|
481
|
-
-
|
|
482
|
-
-
|
|
483
|
-
- [Configuring the PencilKit tool picker](https://sosumi.ai/documentation/pencilkit/configuring-the-pencilkit-tool-picker)
|
|
360
|
+
- [pencilkit patterns](references/pencilkit-patterns.md): picker observer, custom items, delegate lifecycle, undo, thumbnails, comparison, version fallback, configurable SwiftUI wrapper
|
|
361
|
+
- https://developer.apple.com/documentation/pencilkit
|
|
362
|
+
- https://developer.apple.com/documentation/pencilkit/pkcanvasview
|
|
363
|
+
- https://developer.apple.com/documentation/pencilkit/pkdrawing
|
|
364
|
+
- https://developer.apple.com/documentation/pencilkit/pktoolpicker
|
|
365
|
+
- https://developer.apple.com/documentation/pencilkit/pkinkingtool
|
|
366
|
+
- https://developer.apple.com/documentation/pencilkit/pkstroke
|
|
367
|
+
- Apple articles: "Drawing with PencilKit" and "Configuring the PencilKit tool picker", both under the PencilKit documentation
|