-
PaperKit 래핑 해제하기
PaperKit으로 캔버스 기반 애플리케이션을 만들어 보세요. 마크업 요소에 접근하고 이를 생성 및 수정할 수 있도록 해 주는 새로운 데이터 모델 API를 살펴보세요. 맞춤형 제어 항목과 주석을 추가하는 방법을 알아보고, 이러한 기능을 앱에 통합하여 모든 기능을 갖춘 창의적인 캔버스를 빌드하는 모범 사례를 살펴보세요.
챕터
- 0:00 - Introduction
- 1:22 - Data model
- 3:41 - Elements
- 5:17 - Adornments
- 7:11 - Next steps
리소스
관련 비디오
WWDC26
WWDC25
-
비디오 검색…
안녕하세요, Pencil and Paper 팀 엔지니어 Matt입니다 사용자가 원하는 것을 자유롭게 만들 수 있는 캔버스를 제공하는 앱은 Apple 플랫폼에서 가장 상징적이고 강력한 경험 중 하나입니다 PaperKit은 캔버스 경험을 구동하는 프레임워크로 Apple의 여러 자체 앱에서 활용되고 있습니다 아이디어를 스케치하거나 이미지를 삽입하거나 Notes에서 문서를 마크업할 때 그것이 바로 PaperKit입니다 펜슬, 도형, 텍스트, 이미지를 갖춘 완전한 캔버스 경험이며 모두 함께 작동합니다 Preview에서 PDF를 열고 서명을 추가하거나 구절을 강조할 때 중요한 부분에 동그라미를 표시할 때도 PaperKit이 사용됩니다
macOS의 Freeform에서 아이디어를 떠올릴 때도 PaperKit입니다
iOS, macOS, visionOS 27에서 PaperKit이 새롭게 열립니다 오늘은 PaperKit의 기능을 활용하는 방법을 알려드리겠습니다 캔버스 경험을 완전히 제어할 수 있도록 도움을 드릴게요
먼저 데이터 모델부터 살펴보겠습니다 데이터 모델을 통해 캔버스의 모든 요소에 접근할 수 있습니다
그런 다음 도형이나 이미지 같은 요소를 다루는 방법을 알아보겠습니다
마지막으로 어도먼트에 대해 알아보겠습니다 어도먼트를 사용하면 인터랙티브 오버레이와 컨트롤을 추가할 수 있습니다
데이터 모델부터 시작해 볼까요 저는 PaperKit으로 구동되는 만화책 편집기를 만들고 있습니다 기본 템플릿은 이미 준비했습니다 하지만 거기까지가 전부였어요 이제 그 템플릿들을 PaperMarkup으로 변환해야 합니다 PaperMarkup에 새로운 subelements 속성이 추가되었습니다 캔버스의 모든 요소에 MarkupOrderedSet으로 접근할 수 있습니다 읽기 및 쓰기가 가능한 순서 있는 컬렉션입니다 이 코드 스니펫은 각 패널에 대한 도형 요소를 생성합니다
그런 다음 마크업을 업데이트합니다
이게 전부입니다 iPad에서 확인해 볼게요
만화에 3페이지 패널을 추가해 볼게요 좋아요 원하는 대로 나타나고 있네요 캔버스가 완전히 인터랙티브한데 만화책 편집기에서는 문제가 됩니다 패널을 선택하고 드래그하거나 삭제까지 할 수 있거든요
원하는 동작이 아닙니다 템플릿 요소는 편집할 수 없어야 합니다 이를 수정하려면 해당 도형 요소의 동작 방식을 변경해야 합니다
캔버스의 모든 요소는 Markup 프로토콜을 준수합니다
frame과 rotation 같은 공통 속성을 제공합니다
새로운 allowedInteractions 속성도 추가되었습니다 MarkupInteractions 옵션 세트입니다 각 요소에서 수정할 수 있는 항목을 세밀하게 제어할 수 있습니다 Markup interactions는 이동, 크기 조절, 회전을 제어할 수 있습니다 삭제, 스타일 적용, 선택을 개별적으로 또는 조합해서 제어하죠 모든 것을 한꺼번에 잠그고 싶다면 read-only는 모든 것을 단일 플래그로 통합합니다 만화 템플릿에 안성맞춤입니다
만화책 편집기에서 패널과의 인터랙션을 제한하려면 .allowedInteractions를 .readOnly로 설정해야 합니다 시도해 볼게요 이제 패널 테두리를 탭하면 아무 일도 일어나지 않습니다 템플릿 도형이 읽기 전용입니다 말풍선을 추가하고 이리저리 이동할 수도 있습니다
스타일도 적용할 수 있지만
패널은 고정되어 있습니다 완벽합니다 앱의 모습이 잡혀가고 있지만 패널이 더 눈에 띄어야 합니다 그래서 템플릿 스타일 지정을 위해 툴바에 색상 선택기를 추가했습니다
스타일 적용을 구현하기 위해 요소에 대해 자세히 알아보겠습니다 PaperMarkup의 모든 요소에는 구체적인 유형이 있습니다
도형, 이미지, 링크, 루페 및 펜슬 획입니다 이들은 모두 동일한 MarkupOrderedSet의 일부이며 Markup 프로토콜을 준수합니다 하지만 각 유형에는 자체적인 커스텀 속성이 있습니다 도형에 대해 자세히 살펴볼게요 PaperKit은 다양한 도형 유형을 지원합니다 각 유형마다 고유한 속성이 있습니다 둥근 직사각형의 모서리 반지름 같은 곡선의 제어점도 있습니다
만화 패널에는 직사각형을 사용했습니다 직사각형에는 획 색상이 있는데 바로 이게 필요한 속성입니다 패널에 색상을 적용하려면 subelements를 순회해야 합니다
그런 다음 획 색상과 채우기 색상을 설정합니다
더욱 돋보이게 하려면 마크업 배경에도 동일한 색상을 사용하겠습니다 마지막으로 paperMarkupViewController의 마크업을 업데이트합니다 iPad에서 결과를 확인해 볼게요
이렇게 캔버스가 변화합니다 제가 선택한 색상으로 페이지에 스타일이 적용되고 더 개성 있는 모습으로 변해가고 있습니다 PaperKit은 PencilKit 위에 구축되어 Apple Pencil로 그림을 그릴 수 있습니다
각 획은 마크업 요소가 됩니다 PencilKit 모델 API를 모두 활용할 수 있습니다 해당 API는 이제 문자 인식을 지원합니다 그리고 Bézier 경로 변환도 지원합니다 자세한 내용은 "Reading Between the Strokes with PencilKit"을 확인하세요
이제 어도먼트로 커스텀 컨트롤을 추가하는 방법을 알아보겠습니다 각 패널에 사용자가 아트워크를 만들 수 있는 버튼을 추가하고 싶습니다 하지만 이 컨트롤들이 문서의 일부가 되는 것은 원하지 않습니다 저장, 인쇄, 내보내기에 포함되어서는 안 됩니다 편집 중에만 캔버스 위에 존재하면 됩니다 그게 바로 Markup 어도먼트입니다 캔버스 좌표에 고정된 시각적 오버레이입니다 어도먼트는 버튼, 주석, 협업 UI에 이상적입니다 줌과 스크롤을 자동으로 추적하며 저장되는 마크업과 완전히 분리되어 있습니다 각 패널에 대해 MarkupAdornment를 생성합니다 패널 중앙에 고정시키고 imageConfiguration을 통해 SF Symbol 아이콘을 지정합니다
그런 다음 배열을 컨트롤러의 adornments 속성에 할당합니다
탭을 처리하려면 didTapAdornmentWithID 델리게이트 메서드를 구현합니다
사용자가 어도먼트를 탭하면 ImagePlaygroundViewController를 제시합니다
Image Playground에서 이미지가 반환되면 ImageMarkup을 생성합니다
그런 다음 subelements에 삽입하고 뷰 컨트롤러의 마크업을 업데이트합니다 다시 한번 시도해 볼게요
패널 중 하나를 탭해서 아트워크를 만들어 볼게요
저의 만화는 슈퍼히어로 개가 주인공으로 도시에서 범죄와 싸우는 내용이죠
생성된 이미지가 패널을 가득 채웁니다
앱에서 이미지를 생성하는 방법에 대해 더 알아보려면 "Create high-quality images using Image Playground"를 시청하세요 이제 몇 가지 이미지와 텍스트, 폰트를 추가하면 만화의 첫 페이지가 완성됩니다
슈퍼히어로 개가 세상을 구할 거예요 이제 완전히 인터랙티브한 캔버스 기반 경험을 앱에서 PaperKit으로 구현할 수 있습니다 데이터 모델을 사용해 캔버스의 내용을 프로그래밍 방식으로 읽고 수정하세요 어도먼트를 추가해 앱에 맞는 인터랙티브 오버레이를 만드세요 여러분이 PaperKit을 어떻게 펼쳐나가실지 기대됩니다 시청해 주셔서 감사합니다!
-
-
1:36 - Creating markup subelements
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 } -
3:03 - Making template elements read-only
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 } -
4:22 - Apply style to template elements
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 } -
5:53 - Add adornments to each panel
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 } -
6:08 - Handle adornment taps
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) } -
6:20 - Place the generated image
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 }
-
-
- 0:00 - Introduction
Meet PaperKit, the canvas behind Notes, Preview, and Freeform, now open to your apps in iOS, macOS, and visionOS 27 — covering the data model, elements, and adornments.
- 1:22 - Data model
PaperMarkup's new subelements property exposes every canvas element as a readable, writable ordered set, and allowedInteractions gives fine-grained control over what each element permits.
- 3:41 - Elements
Each element has a concrete type — shapes, images, links, loupes, and pencil strokes — with its own properties, and PaperKit builds on PencilKit so Apple Pencil strokes become markup elements.
- 5:17 - Adornments
Markup adornments are visual overlays anchored to canvas coordinates — ideal for buttons, annotations, and collaboration UI — that track zoom and scroll and stay separate from persisted markup.
- 7:11 - Next steps
Building a fully interactive canvas experience with PaperKit — using the data model to read and modify canvas contents, and adding adornments for interactive overlays tailored to your app.