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 应用中使用 WebKit for SwiftUI 嵌入和控制网页内容。

Software Development1.1K2个月前更新