ハイライト
PaperKit は、Apple が Notes、Preview、Freeform などの App で内部的に使っているキャンバスエンジンです。iOS、macOS、visionOS 27 以降、開発者にも公開され、完全なキャンバスデータモデル、要素操作、カスタム装飾レイヤーを利用できます。
主要内容
以前、App 内に描画、マークアップ、画像のドラッグに対応したキャンバスを作ろうとすると、タッチイベント、描画ロジック、Undo/Redo、ジェスチャ認識、レイヤー管理などを自前で扱う必要がありました。作業量は大きく、完成した体験もシステム標準 App と同じ品質にそろえるのは難しいものでした。
PaperKit は、この問題を解決するための Apple の統一フレームワークです。単純な描画ツールの代わりに、ペン、図形、テキスト、画像が一体として動く完全なキャンバス体験を提供します。Notes でスケッチを描く、Preview で署名やハイライトを追加する、Freeform でブレインストーミングする。こうした体験の背後には PaperKit があります。
このフレームワークの中核は3つの層に分かれます。
データモデル層:PaperMarkup はキャンバス上のすべての要素を順序付きコレクション subelements として公開します。任意の要素を読み取り、作成し、変更できます。
要素制御層:各要素は Markup プロトコルに準拠し、共通の frame、rotation プロパティと、型ごとの固有プロパティを持ちます。新しい allowedInteractions を使うと、各要素に対して移動、サイズ変更、回転、削除、スタイル変更、選択を個別に制御できます。
装飾レイヤー(Adornments):ボタン、注釈、共同作業マーカーなどの一時的な UI コントロールをキャンバス上に重ねられます。これらのコントロールはドキュメントの一部にならず、保存や書き出しもされません。編集時だけ存在し、ズームやスクロールに自動追従します。
詳細
データモデルと subelements
PaperKit の中核は、キャンバス全体の内容を表す PaperMarkup です。([01:36](https://developer.apple.com/videos/play/wwdc2026/372/?time=96))
import PaperKit
func generateMarkup(pageSize: CGSize, panelFrames: [CGRect], configuration: ShapeConfiguration) -> PaperMarkup {
var markup = PaperMarkup(bounds: CGRect(origin: .zero, size: pageSize))
var subelements: MarkupOrderedSet = markup.subelements
for panelFrame: CGRect in panelFrames {
let shape = ShapeMarkup(frame: panelFrame, configuration: configuration)
subelements.append(shape)
}
markup.subelements = subelements
return markup
}
重要な点:
PaperMarkupの初期化にはキャンバスサイズを定義するboundsが必要です。subelementsはMarkupOrderedSet型の読み書き可能な順序付きコレクションです。ShapeMarkupは図形要素で、初期化には位置frameと設定configurationが必要です。subelementsを変更した後は、markup.subelementsに代入し直すことで変更が反映されます。
要素のインタラクションを制御する
デフォルトでは、キャンバス上の要素は自由に選択、ドラッグ、削除できます。ただし、テンプレート要素をロックしたい場面もあります。([03:03](https://developer.apple.com/videos/play/wwdc2026/372/?time=183))
import PaperKit
func generateMarkup(pageSize: CGSize, panelFrames: [CGRect], configuration: ShapeConfiguration) -> PaperMarkup {
var markup = PaperMarkup(bounds: CGRect(origin: .zero, size: pageSize))
var subelements: MarkupOrderedSet = markup.subelements
for panelFrame: CGRect in panelFrames {
var shape = ShapeMarkup(frame: panelFrame, configuration: configuration)
shape.allowedInteractions = .readOnly
subelements.append(shape)
}
markup.subelements = subelements
return markup
}
重要な点:
allowedInteractionsはMarkupInteractionsのオプションセットです。.readOnlyは移動、サイズ変更、回転、削除、スタイル変更、選択をまとめて無効化する組み合わせフラグです。- 移動だけ許可して削除は禁止する、といった個別制御もできます。
スタイル変更
すべての要素には型ごとのプロパティがあります。ShapeMarkup には strokeColor と fillColor があります。([04:22](https://developer.apple.com/videos/play/wwdc2026/372/?time=262))
import PaperKit
func updatePanelColor(_ selectedColor: CGColor) {
guard var markup: PaperMarkup = paperMarkupViewController.markup else { return }
var subelements: MarkupOrderedSet = markup.subelements
for element in subelements {
guard var shape = element as? ShapeMarkup else { continue }
shape.strokeColor = selectedColor
shape.fillColor = selectedColor.copy(alpha: 0.15)
subelements.updateOrAppend(shape)
}
markup.subelements = subelements
markup.backgroundColor = selectedColor.copy(alpha: 0.15)
paperMarkupViewController.markup = markup
}
重要な点:
subelementsにはさまざまなMarkup準拠型が入るため、反復時には型変換が必要です。updateOrAppendは既存要素を更新するか、新しい要素を追加します。- 変更後は
paperMarkupViewController.markupに代入し直します。
装飾レイヤー(Adornments)
Adornments はキャンバス上に重ねる一時的なコントロールで、ドキュメントには保存されません。ボタン、注釈、共同作業 UI に向いています。([05:53](https://developer.apple.com/videos/play/wwdc2026/372/?time=353))
import PaperKit
func addPanelAdornments(for page: Page) {
var adornments: [MarkupAdornment] = []
for (panelIndex, panel) in page.panels.enumerated() {
let adornmentID = UUID()
adornmentPanelMapping[adornmentID] = panelIndex
let center = CGPoint(x: panel.midX, y: panel.midY)
let adornment = MarkupAdornment(
id: adornmentID,
anchor: .canvas(location: center),
imageConfiguration: .systemImage("photo.badge.plus"),
dragRegion: .fixed,
scalesWithZoom: false
)
adornments.append(adornment)
}
paperMarkupViewController.adornments = adornments
}
重要な点:
MarkupAdornmentはUUIDで一意に識別し、後続のタップ処理に使います。anchorは.canvas(location:)でキャンバス座標に固定され、ズームやスクロールに自動追従します。imageConfigurationは SF Symbols をサポートします。dragRegion: .fixedはボタン位置を固定し、scalesWithZoom: falseはズームしても拡大しないことを表します。
装飾レイヤーのタップを処理する
import ImagePlayground
import PaperKit
func paperMarkupViewController(_ paperMarkupViewController: PaperMarkupViewController, didTapAdornmentWithID id: UUID) {
guard let panelIndex = adornmentPanelMapping[id] else { return }
activeImageGenerationPanelIndex = panelIndex
let imagePlaygroundViewController = ImagePlaygroundViewController()
imagePlaygroundViewController.delegate = self
present(imagePlaygroundViewController, animated: true)
}
重要な点:
- デリゲートメソッドを実装して装飾レイヤーのタップイベントを受け取ります。
idを具体的な業務オブジェクト、たとえば漫画パネルのインデックスに対応付けます。- 必要に応じて Image Playground など別の画面を表示できます。
生成された画像を挿入する
import ImagePlayground
import PaperKit
func imageViewController(_ imageViewController: ImagePlaygroundViewController, didCreateImageAt imageURL: URL) {
guard let panelFrame = activeGenerationPanelFrame,
let paperMarkupViewController = pageViewController.paperViewController,
var markup = paperMarkupViewController.markup,
let image = UIImage(contentsOfFile: imageURL.path) else { return }
let imageMarkup = ImageMarkup(frame: panelFrame, image: image)
markup.subelements.append(imageMarkup)
paperMarkupViewController.markup = markup
}
重要な点:
ImageMarkupは画像要素を作成するために使い、位置とUIImageが必要です。subelementsに直接appendすると挿入できます。markupを更新するとビューが再描画されます。
PencilKit との関係
PaperKit は PencilKit の上に構築されており、Apple Pencil の描画をサポートします。各ストロークは markup 要素になり、PencilKit のモデル API を利用できます。これらの API は、現在では文字認識とベジェ曲線パス変換にも対応しています。
重要な示唆
漫画制作 App:デモのような漫画エディタを作れます。allowedInteractions でパネルレイアウトをロックし、adornments で「画像を生成」ボタンを追加し、Image Playground と組み合わせてユーザーがすばやく制作できるようにします。入口:ShapeMarkup + MarkupAdornment。
塗り絵 App:read-only の線画テンプレートを用意し、ユーザーは指定領域だけを塗れるようにします。subelements を反復して特定の図形を見つけ、fillColor を変更します。
共同注釈ツール:adornments を使って共同作業者のオンライン状態、カーソル位置、コメントマーカーを表示します。これらの情報はドキュメントには保存されず、リアルタイムに更新できます。入口:MarkupAdornment + 定期更新。
手書きノート検索:PencilKit の文字認識 API を使って手書き内容をテキストに変換し、TextMarkup 要素として挿入することで、ノートを検索可能にします。
テンプレート入力ツール:請求書、契約書、フォームなどの各種ドキュメントテンプレートを作成し、readOnly でテンプレート構造をロックします。ユーザーは空欄部分だけに入力できます。
関連セッション
- 203 - PencilKit をさらに活用する — PaperKit は PencilKit の上に構築されています。文字認識とベジェパス変換の詳細を学べます
- 375 - Image Playground で高品質な画像を作成する — App に Image Playground の画像生成を統合する方法
- 269 - SwiftUI — PaperKit の View Controller を SwiftUI と統合する方法
- 241 - Foundation Models — キャンバス内容と組み合わせられる Apple の基盤モデル機能
- 277 - WidgetKit — キャンバス内容をレンダリングして Widget に表示する方法
コメント
GitHub Issues · utterances