Paperkit

作者 dpearson26998d90fd121a26無授權條款1.1K 個星標收錄於 2026年10月8日更新於 2026年10月8日儲存庫2 個月前更新

Add drawings, shapes, and a consistent markup experience using PaperKit. Use when integrating PaperMarkupViewController for markup editing, adding shape recognition, working with PaperMarkup data models, embedding markup tools in document editors, or building annotation features that need the system-standard markup toolbar. New in iOS 26.

AI 產生的概覽

指導開發者整合 Apple PaperKit 框架,在 iOS 26 應用程式中實現標記、形狀與繪圖功能。

功能
此技能為使用 Apple PaperKit 框架提供參考指引,涵蓋 PaperMarkupViewController、PaperMarkup 資料模型、插入控制器、FeatureSet 設定、PencilKit 整合以及 SwiftUI 橋接。內容包含程式碼範例、常見錯誤,以及用於建構標記與註解功能的審查清單。它產出的是面向開發者的說明,而非檔案或成品。
適用情境
適用於整合 PaperMarkupViewController 進行標記編輯、加入形狀辨識、使用 PaperMarkup 資料模型,或在文件編輯器中嵌入標記工具的情境。鎖定需要系統標準標記工具列、目標平台為 iOS、iPadOS、macOS 或 visionOS 26 的應用程式。
執行需求
需要 Apple 平台開發環境,支援 iOS 26+、iPadOS 26+、Mac Catalyst 26+、macOS 26+ 或 visionOS 26+,並需 PaperKit 與 PencilKit 框架。此技能不含指令碼,僅為說明文件,另附一份選用參考檔案。

PaperKit

Beta-sensitive. PaperKit is new in iOS/iPadOS 26, macOS 26, and visionOS 26. API surface may change. Verify details against current Apple documentation before shipping.

PaperKit combines PencilKit drawing with structured markup elements such as shapes, text, images, and lines in a canvas managed by PaperMarkupViewController.

Contents

Workflow

  1. Choose the document bounds, supported FeatureSet, and persistence version before constructing UI.
  2. Create PaperMarkup, embed PaperMarkupViewController, and keep the controller, tool picker, and insertion controller alive for the view lifetime.
  3. Use the platform-appropriate insertion surface and keep PencilKit drawing inside the PaperKit document boundary.
  4. Save off the main thread, retain a thumbnail for forward-incompatible content, and test round-trip loading with the same feature set.
  5. On failure, restore the original document bytes, fix the feature-set/version/controller mismatch, and rerun edit, save, relaunch, load, thumbnail fallback, and undo checks.

Load references/paperkit-patterns.md [blocked] for full platform setup, tool picker wiring, persistence, thumbnails, custom feature sets, programmatic construction, and migration.

Setup

PaperKit requires no entitlements or special Info.plist entries.

swift
import PaperKit

Platform availability: iOS 26.0+, iPadOS 26.0+, Mac Catalyst 26.0+, macOS 26.0+, visionOS 26.0+.

Three core components:

ComponentRole
PaperMarkupViewControllerInteractive canvas for creating and displaying markup and drawing
PaperMarkupData model for serializing all markup elements and PencilKit drawing
MarkupEditViewController / MarkupToolbarViewControllerInsertion UI for adding markup elements

PaperMarkupViewController

The primary view controller for interactive markup. Provides a scrollable canvas for freeform PencilKit drawing and structured markup elements. Conforms to Observable and PKToolPickerObserver.

Basic UIKit Setup

swift
import PaperKitimport PencilKitimport UIKit
class MarkupViewController: UIViewController, PaperMarkupViewController.Delegate {    var paperVC: PaperMarkupViewController!    var toolPicker: PKToolPicker!
    override func viewDidLoad() {        super.viewDidLoad()
        let pageBounds = CGRect(origin: .zero, size: CGSize(width: 612, height: 792))        let markup = PaperMarkup(bounds: pageBounds)        let features = FeatureSet.latest
        paperVC = PaperMarkupViewController(            markup: markup,            supportedFeatureSet: features        )        paperVC.delegate = self
        addChild(paperVC)        paperVC.view.frame = view.bounds        paperVC.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]        view.addSubview(paperVC.view)        paperVC.didMove(toParent: self)
        toolPicker = PKToolPicker()        toolPicker.addObserver(paperVC)        paperVC.pencilKitResponderState.activeToolPicker = toolPicker        paperVC.pencilKitResponderState.toolPickerVisibility = .visible    }
    func paperMarkupViewControllerDidChangeMarkup(        _ controller: PaperMarkupViewController    ) {        guard let markup = controller.markup else { return }        Task { try await save(markup) }    }}

Key Properties

PropertyTypeDescription
markupPaperMarkup?The current data model
selectedMarkupPaperMarkupCurrently selected content
isEditableBoolWhether the canvas accepts input
isRulerActiveBoolWhether the ruler overlay is shown
drawingToolany PKToolActive PencilKit drawing tool
contentViewUIView? / NSView?Background view rendered beneath markup
zoomRangeClosedRange<CGFloat>Min/max zoom scale
supportedFeatureSetFeatureSetEnabled PaperKit features

Touch Modes

PaperMarkupViewController.TouchMode has two cases: .drawing and .selection.

swift
paperVC.directTouchMode = .drawing    // Finger drawspaperVC.directTouchMode = .selection  // Finger selects elementspaperVC.directTouchAutomaticallyDraws = true  // System decides based on Pencil state

Content Background

Set any view beneath the markup layer for templates, document pages, or images being annotated. Keep the PaperMarkup(bounds:) coordinate space aligned to the background content, such as a PDF page or rendered image size, so saved annotations restore in the right place:

swift
let pageBounds = CGRect(origin: .zero, size: pageImage.size)let imageView = UIImageView(image: pageImage)imageView.frame = pageBounds
let markup = PaperMarkup(bounds: pageBounds)paperVC = PaperMarkupViewController(markup: markup, supportedFeatureSet: features)paperVC.contentView = imageView

Delegate Callbacks

MethodCalled when
paperMarkupViewControllerDidChangeMarkup(_:)Markup content changes
paperMarkupViewControllerDidBeginDrawing(_:)User starts drawing
paperMarkupViewControllerDidChangeSelection(_:)Selection changes
paperMarkupViewControllerDidChangeContentVisibleFrame(_:)Visible frame changes

PaperMarkup Data Model

PaperMarkup is a Sendable struct that stores all markup elements and PencilKit drawing data.

Creating and Persisting

swift
// New empty model. Bounds define the saved document coordinate space.let markup = PaperMarkup(bounds: CGRect(x: 0, y: 0, width: 612, height: 792))
// Load from saved datalet markup = try PaperMarkup(dataRepresentation: savedData)
// Save — dataRepresentation() is async throwsfunc save(_ markup: PaperMarkup) async throws {    let data = try await markup.dataRepresentation()    try data.write(to: fileURL)}

Inserting Content Programmatically

swift
// Text boxmarkup.insertNewTextbox(    attributedText: AttributedString("Annotation"),    frame: CGRect(x: 50, y: 100, width: 200, height: 40),    rotation: 0)
// Imagemarkup.insertNewImage(cgImage, frame: CGRect(x: 50, y: 200, width: 300, height: 200), rotation: 0)
// Shapelet shapeConfig = ShapeConfiguration(    type: .rectangle,    fillColor: UIColor.systemBlue.withAlphaComponent(0.2).cgColor,    strokeColor: UIColor.systemBlue.cgColor,    lineWidth: 2)markup.insertNewShape(configuration: shapeConfig, frame: CGRect(x: 50, y: 420, width: 200, height: 100), rotation: 0)
// Line with arrow end markerlet lineConfig = ShapeConfiguration(type: .line, fillColor: nil, strokeColor: UIColor.red.cgColor, lineWidth: 3)markup.insertNewLine(    configuration: lineConfig,    from: CGPoint(x: 50, y: 550), to: CGPoint(x: 250, y: 550),    startMarker: false, endMarker: true)

Shape types: .rectangle, .roundedRectangle, .ellipse, .line, .arrowShape, .star, .chatBubble, .regularPolygon.

Other Operations

swift
markup.append(contentsOf: otherMarkup)       // Merge another PaperMarkupmarkup.append(contentsOf: pkDrawing)          // Merge a PKDrawingmarkup.transformContent(CGAffineTransform(...)) // Apply affine transformmarkup.removeContentUnsupported(by: featureSet) // Strip unsupported elements
PropertyDescription
boundsCoordinate space of the markup
contentsRenderFrameTight bounding box of all content
featureSetFeatures used by this data model's content
indexableContentExtractable text for search indexing

Use suggestedFrameForInserting(contentInFrame:) on the view controller to get a frame that avoids overlapping existing content.

Insertion Controllers

MarkupEditViewController (iOS, iPadOS, Mac Catalyst, visionOS)

Presents a popover menu for inserting shapes, text boxes, lines, and other elements.

swift
func showInsertionMenu(from barButtonItem: UIBarButtonItem) {    let editVC = MarkupEditViewController(        supportedFeatureSet: paperVC.supportedFeatureSet,        additionalActions: []    )    editVC.delegate = paperVC  // PaperMarkupViewController conforms to the delegate    editVC.modalPresentationStyle = .popover    editVC.popoverPresentationController?.barButtonItem = barButtonItem    present(editVC, animated: true)}

MarkupToolbarViewController (macOS, Mac Catalyst)

Provides a toolbar with drawing tools and insertion buttons. Use it for native macOS and for Mac Catalyst toolbar-style UI; Catalyst apps that want a UIKit popover can use MarkupEditViewController.

swift
let toolbar = MarkupToolbarViewController(supportedFeatureSet: paperVC.supportedFeatureSet)toolbar.delegate = paperVCaddChild(toolbar)toolbar.view.frame = toolbarContainerView.boundstoolbarContainerView.addSubview(toolbar.view)toolbar.didMove(toParent: self)

Both controllers must use the same FeatureSet as the PaperMarkupViewController.

FeatureSet Configuration

FeatureSet controls which markup capabilities are available.

PresetDescription
.latestAll current features — recommended starting point
.version1Features from version 1
.emptyNo features enabled

Customizing

swift
var features = FeatureSet.latestfeatures.remove(.stickers)features.remove(.images)
// Or build up from emptyvar features = FeatureSet.emptyfeatures.insert(.drawing)features.insert(.text)features.insert(.shapeStrokes)

Available Features

FeatureDescription
.drawingFreeform PencilKit drawing
.textText box insertion
.imagesImage insertion
.stickersSticker insertion
.linksLink annotations
.loupesLoupe/magnifier elements
.shapeStrokesShape outlines
.shapeFillsShape fills
.shapeOpacityShape opacity control

HDR Support

Set colorMaximumLinearExposure above 1.0 on both the FeatureSet and PKToolPicker:

swift
var features = FeatureSet.latestfeatures.colorMaximumLinearExposure = 4.0toolPicker.colorMaximumLinearExposure = features.colorMaximumLinearExposure

Use view.window?.windowScene?.screen.potentialEDRHeadroom to match the device screen's capability. Use 1.0 for SDR-only.

Shapes, Inks, and Line Markers

swift
features.shapes = [.rectangle, .ellipse, .arrowShape, .line]features.inks = [.pen, .pencil, .marker]features.lineMarkerPositions = .all  // .single, .double, .plain, or .all

Integration with PencilKit

PaperKit accepts PKTool for drawing and can append PKDrawing content.

PaperKit is not a drop-in replacement for a low-level PKCanvasView when the app depends on custom brush behavior, raw PKDrawing / PKStroke analytics, or custom lasso-centric editing. Keep those workflows owned by PencilKit, and add PaperKit beside them for structured review markup such as callouts, arrows, text boxes, labels, image stamps, and system-standard insertion UI. Migrate or duplicate existing drawings into a PaperKit annotation layer with PaperMarkup.append(contentsOf: PKDrawing) only when the low-level editing path no longer needs to own that content.

swift
import PencilKit
// Set drawing toolpaperVC.drawingTool = PKInkingTool(.pen, color: .black, width: 3)
// Merge existing PKDrawing into markupmarkup.append(contentsOf: existingPKDrawing)

Tool Picker Setup

swift
let toolPicker = PKToolPicker()toolPicker.addObserver(paperVC)paperVC.pencilKitResponderState.activeToolPicker = toolPickerpaperVC.pencilKitResponderState.toolPickerVisibility = .visible

Setting toolPickerVisibility to .hidden keeps the picker functional (responds to Pencil gestures) but not visible, enabling the mini tool picker experience.

Content Version Compatibility

FeatureSet.ContentVersion maps to PKContentVersion:

swift
let pkVersion = features.contentVersion.pencilKitContentVersion

SwiftUI Integration

Wrap PaperMarkupViewController in UIViewControllerRepresentable:

swift
struct MarkupView: UIViewControllerRepresentable {    @Binding var markup: PaperMarkup    let features: FeatureSet
    func makeUIViewController(context: Context) -> PaperMarkupViewController {        let vc = PaperMarkupViewController(markup: markup, supportedFeatureSet: features)        vc.delegate = context.coordinator        let toolPicker = PKToolPicker()        toolPicker.addObserver(vc)        vc.pencilKitResponderState.activeToolPicker = toolPicker        vc.pencilKitResponderState.toolPickerVisibility = .visible        context.coordinator.toolPicker = toolPicker        return vc    }
    func updateUIViewController(_ vc: PaperMarkupViewController, context: Context) {        if vc.markup != markup { vc.markup = markup }    }
    func makeCoordinator() -> Coordinator { Coordinator(parent: self) }
    class Coordinator: NSObject, PaperMarkupViewController.Delegate {        let parent: MarkupView        var toolPicker: PKToolPicker?        init(parent: MarkupView) { self.parent = parent }
        func paperMarkupViewControllerDidChangeMarkup(            _ controller: PaperMarkupViewController        ) {            if let markup = controller.markup { parent.markup = markup }        }    }}

Initialize the bound PaperMarkup from the document or page size before creating the SwiftUI bridge:

swift
struct DocumentMarkupScreen: View {    let pageSize: CGSize    @State private var markup: PaperMarkup    private let features = FeatureSet.latest
    init(pageSize: CGSize) {        self.pageSize = pageSize        _markup = State(            initialValue: PaperMarkup(                bounds: CGRect(origin: .zero, size: pageSize)            )        )    }
    var body: some View {        MarkupView(markup: $markup, features: features)    }}

Common Mistakes

MistakeFix
View, insertion UI, and saved document use mismatched feature setsChoose one supported FeatureSet and use it across the editing session.
Loaded content is assigned without a version checkVerify markup.featureSet.isSubset(of: supportedFeatureSet) or show the saved thumbnail/fallback.
Serialization blocks UI or overlaps unsafelyAwait dataRepresentation() off the interaction path and debounce autosaves.
Tool picker is a local variableRetain it for the controller/view lifetime.
Wrong insertion surface for the platformUse MarkupToolbarViewController on macOS; use MarkupEditViewController on UIKit, with Catalyst supporting either presentation.

Review Checklist

  • import PaperKit present; deployment target is iOS 26+ / macOS 26+ / visionOS 26+
  • PaperMarkup initialized with bounds matching content size
  • Same FeatureSet used for PaperMarkupViewController and insertion controller
  • dataRepresentation() called in async context
  • PKToolPicker retained as a stored property
  • Delegate set on PaperMarkupViewController for change callbacks
  • Content version checked when loading saved data
  • Correct insertion controller per platform (MarkupToolbarViewController for macOS/Catalyst toolbar UI; MarkupEditViewController for UIKit/Catalyst popovers)
  • MarkupError cases handled on deserialization
  • HDR: colorMaximumLinearExposure set on FeatureSet and PKToolPicker.colorMaximumLinearExposure

References

來源與署名

來源:dpearson2699/swift-ios-skills位於skills/paperkit提交8d90fd1

授權條款: 無授權條款

內容歸原作者所有。SourceWeft 從公開儲存庫中收錄這些內容。

檢舉或申請下架

更多來自 dpearson2699/swift-ios-skills 的技能

Widgetkit

dpearson2699

指導實作、審查與改進 iOS、iPadOS、watchOS 與 CarPlay 上的 WidgetKit 小工具與控制項。

Software Development1.1K2 個月前更新

Weatherkit

dpearson2699

指導 iOS 開發者使用 WeatherService 取得 WeatherKit 預報、警報與署名資訊。

Software Development1.1K2 個月前更新

Vision Framework

dpearson2699

Implement computer vision features including text recognition (OCR), face detection, barcode scanning, image segmentation, object tracking, and document scanning in iOS apps. Covers both the modern Swift-native Vision API (iOS 18+) and legacy VNRequest patterns, VisionKit DataScannerViewController for live camera scanning, and CoreMLRequest/VNCoreMLRequest for custom model inference. Use when adding OCR, barcode scanning, face detection, or custom Core ML model inference with Vision.

待分類1.1K2 個月前更新

Tipkit

dpearson2699

Implement and review Apple TipKit feature-discovery UI for iOS 17+ apps. Use when adding or auditing in-app tips, contextual help, coach marks, Tip, TipView, popoverTip, rules, events, actions, display frequency, testing overrides, reusable tip identifiers, or iOS 18+ TipGroup and CloudKit tip sync; avoid for generic SwiftUI navigation or layout outside tip presentation.

待分類1.1K2 個月前更新

Tabletopkit

dpearson2699

指導使用 TabletopKit 在 visionOS 上打造多人空間桌遊,涵蓋棋具、座位、動作與 RealityKit 算繪。

Software Development1.1K2 個月前更新

Swiftui Webkit

dpearson2699

指導在 iOS 26 及更新版本的 SwiftUI App 中使用 WebKit for SwiftUI 嵌入與控制網頁內容。

Software Development1.1K2 個月前更新