PaperKit is Apple's full canvas framework β previously powering Notes, Freeform, and Preview internally β now opened to third-party developers in iOS/macOS/visionOS 27. It provides a complete markup canvas with pencil strokes, shapes, images, text, and interactive adornments.
β’ Developers can now embed the same canvas engine used by Notes and Freeform directly into their apps, without building from scratch
β’ Fine-grained element interaction control (move, resize, delete, style, select) via MarkupInteractions lets you lock or expose exactly the right editing surface
β’ Markup adornments provide a coordinate-tracked overlay system for custom UI (buttons, collaboration cursors, annotations) that stays separate from the persisted document
Demonstrates creating a PaperKit canvas with programmatically added read-only shape elements for comic panels, plus interactive adornments that appear as overlay buttons anchored to canvas coordinates.
import SwiftUI
import PaperKit
// MARK: - SwiftUI wrapper for PaperMarkupViewController
struct ComicCanvasView: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> ComicEditorViewController {
ComicEditorViewController()
}
func updateUIViewController(_ uiViewController: ComicEditorViewController, context: Context) {}
}
// MARK: - Main editor
class ComicEditorViewController: UIViewController, PaperMarkupViewControllerDelegate {
private let markupVC = PaperMarkupViewController()
override func viewDidLoad() {
super.viewDidLoad()
addChild(markupVC)
view.addSubview(markupVC.view)
markupVC.view.frame = view.bounds
markupVC.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
markupVC.didMove(toParent: self)
markupVC.delegate = self
setupComicPanels()
}
private func setupComicPanels() {
var markup = markupVC.markup
// Define three comic panel rects
let panelFrames: [CGRect] = [
CGRect(x: 20, y: 20, width: 340, height: 220),
CGRect(x: 20, y: 260, width: 160, height: 200),
CGRect(x: 200, y: 260, width: 160, height: 200)
]
var adornments: [MarkupAdornment] = []
for (index, frame) in panelFrames.enumerated() {
// Create a rectangle shape for each panel
var shape = RectangleMarkup()
shape.frame = frame
shape.strokeColor = UIColor.systemIndigo
shape.fillColor = UIColor.systemIndigo.withAlphaComponent(0.08)
// Lock panels so users cannot move, resize, or delete them
shape.allowedInteractions = .readOnly
markup.subelements.append(shape)
// Add an adornment button anchored to the panel center
let center = CGPoint(x: frame.midX, y: frame.midY)
var adornment = MarkupAdornment(id: "panel-\(index)")
adornment.canvasPosition = center
adornment.imageConfiguration = UIImage.SymbolConfiguration(pointSize: 28)
adornment.image = UIImage(systemName: "plus.circle.fill")
adornments.append(adornment)
}
// Commit elements and adornments
markupVC.markup = markup
markupVC.adornments = adornments
}
// MARK: - PaperMarkupViewControllerDelegate
func paperMarkupViewController(
_ controller: PaperMarkupViewController,
didTapAdornmentWithID id: String
) {
// Identify which panel was tapped and respond
print("User tapped adornment: \(id) β present image picker or drawing tool here")
}
}
// MARK: - App entry point
struct ComicApp: App {
var body: some Scene {
WindowGroup {
ComicCanvasView()
.ignoresSafeArea()
}
}
}PaperKit was previously private/internal to Apple; no public API existed before iOS 27. Adornments are not persisted or exported β they are runtime-only overlays. PaperKit is built on PencilKit so PencilKit model APIs (BΓ©zier paths, character recognition) are also available for strokes.
Apple Pencil input supported on compatible iPad and Apple Vision Pro hardware; core canvas works on all devices
More iOS 27 APIs land every week.
Get notified when new capabilities are published β no noise, just signal.