ハイライト
Apple はオンデバイス手書き認識エンジン
PKStrokeRecognizerをサードパーティ開発者に公開しました。29言語をサポートし、ベジェパスとの相互変換、ストローク分割、安定した Stroke ID も追加されます。PencilKit はキャンバス部品から、任意のレンダリングパイプラインに組み込める低レベルツールへ進化します。
主要内容
手書き認識:システム専用から誰でも使える機能へ
iOS 27 より前は、Apple 最高水準のオンデバイス手書き認識を使えるのは Freeform と Notes だけでした。サードパーティ App は高価な外部 SDK を組み込むか、Vision framework で自前につなぎ合わせるしかなく、効果はシステムネイティブには遠く及びませんでした。
現在、PKStrokeRecognizer はすべての開発者に開放されました。これは Swift Actor で、すべてのメソッドは非同期です。認識処理がメインスレッドをブロックすることはありません。モデルは完全にオフラインで動作し、システムに内蔵されているため、ネットワーク接続も追加ストレージも不要です。
(01:46)
PKStrokeRecognizer は3つの中核機能を提供します。
- Recognized text:最も可能性の高い認識結果を返し、ユーザーが何を書いたかをリアルタイム表示する用途に適しています
- Indexable content:すべての候補語を連結した文字列を返し、Spotlight インデックスに適しています
- Search:キャンバス内でキーワードを検索し、一致領域の境界ボックスを返します
(03:41)
PKCanvasView の壁を越える
PencilKit は内部で三次一様 B スプライン(B-spline)を使ってストロークパスを保存します。この表現は描画には向いていますが、業界でより一般的なベジェパスとは互換性がありません。
iOS 27 ではパス変換 API が追加され、PKStrokePath とベジェパスの間でロスレスな相互変換ができます。つまり、自分のレンダリングパイプライン内のベジェパスを PKStrokePath に変換し、PKStrokeRecognizer に渡して認識させることができます。PKCanvasView はまったく不要です。
(08:38)
より開かれた描画モデル
iOS 27 では PKStroke と PKStrokePath に Identifiable プロトコル対応が追加され、安定した UUID が返ります。Undo、Redo、変形の後でも、同じストロークの ID は変わりません。
PKCanvasView には canvasViewSelectionDidChange デリゲートメソッドが追加され、選択範囲が変わると自動的に発火します。renderGroupID も内部実装から制御可能なプロパティになり、どのストロークを「乾いていないインク」としてブレンドレンダリングするかを手動で制御できます。
(10:32)
ストローク分割
iOS 27 では2種類の分割 API が導入されました。
- Programmatic erasing:
PKStrokePathを消しゴムパスとして使い、プログラムからストロークを切断します。ユーザーがキャンバス上で消しゴムを使ったときと同じ効果になります - Substroke extraction:パラメータ化された添字を使って、ストロークから任意の部分を抽出し、テクスチャ粒子の一貫性を保ちます
複雑な描画、たとえば数千個の Stroke を含む場合、分割操作はコストが大きいため、バックグラウンドスレッドで実行することが推奨されます。
(11:36)
詳細
基本的な手書き認識
(03:53)
import PencilKit
let recognizer = PKStrokeRecognizer()
await recognizer.updateDrawing(drawing)
myLabel.text = await recognizer.recognizedText()
重要な点:
PKStrokeRecognizerは Actor であり、スレッドセーフです。updateDrawingとrecognizedTextはどちらも非同期メソッドです。- デフォルトではデバイス言語で認識しますが、
preferredLanguagesで明示的に設定することもできます。 - 29言語をサポートし、
supportedLanguagesで確認できます。 - Simulator はラテン文字系言語のみをサポートします。中国語、日本語、韓国語などは実機テストが必要です。
インデックス可能な内容を生成する
(05:22)
import PencilKit
let recognizer = PKStrokeRecognizer()
await recognizer.updateDrawing(drawing)
if let indexedContent = await recognizer.indexableContent {
index(text: indexedContent)
}
重要な点:
indexableContentはすべての候補語を連結した文字列を返し、Spotlight インデックスに適しています。- 手書きに曖昧さがある場合、たとえば “1” と “l” のようなケースでは、
recognizedTextは最良の答えを1つだけ返し、indexableContentはすべての候補を返します。 recognizerVersionプロパティは現在の認識モデルバージョンを示します。インデックス内容と一緒に永続化し、読み込み時にバージョンを比較してインデックス再構築が必要か判断するのがおすすめです。- ストローク更新のたびに認識器を呼ぶと電力を消費するため、throttle するのがよいです。
キャンバス内検索
(06:58)
import PencilKit
let recognizer = PKStrokeRecognizer()
await recognizer.updateDrawing(drawing)
let results = await recognizer.search("apple")
for result in results {
highlight(bounds: result.bounds)
}
重要な点:
searchはSearchResultの配列を返し、各結果には一致領域のboundsが含まれます。UIFindInteractionと組み合わせることで、システムレベルの検索・置換体験を実装できます。- 検索機能はアクセシビリティの基盤にもなります。VoiceOver は手書き内容を読み上げられ、検索によってスクリーンリーダーは具体的な単語に位置づけられます。
重要な示唆
-
子ども向け識字 App を作る:
PKStrokeRecognizerを使い、子どもが正しい漢字を書いているかをリアルタイムで確認します。searchと組み合わせて筆順を判断し、Substroke extraction で書く過程を再生できます。入口 API:PKStrokeRecognizer.recognizedText()、PKStroke.subscript(_:)。 -
手書き対応ノート App を作る:ユーザーの手書き内容を
indexableContentで Core Spotlight に同期し、全文検索で手書きノートも対象にします。入口 API:PKStrokeRecognizer.indexableContent、recognizerVersion。 -
カスタムキャンバスを作る:Metal や SwiftUI Canvas で独自のレンダリング層を作り、ベジェ相互変換によってストロークデータを
PKStrokeRecognizerに渡します。入口 API:PKStrokePathのベジェ変換メソッド。 -
スマート注釈ツールを作る:Programmatic erasing API を使って「線で消す」ジェスチャを実装します。ユーザーが文字の上に線を引くと、対応する内容を自動認識して削除します。入口 API:
PKStrokePathを消しゴムパスとして使う分割メソッド。 -
手書き数式計算機を作る:ユーザーが手書きした数式を認識し、LaTeX に変換するか、
PKStrokeRecognizerで数字と演算子を直接認識して計算します。入口 API:PKStrokeRecognizer.preferredLanguages、recognizedText()。
関連セッション
- PaperKit に出会う — PaperKit は PencilKit の上に構築されており、このセッションのすべての新 API は PaperKit でも利用可能
- SwiftUI — SwiftUI で PencilKit キャンバスを統合する方法を学ぶ
- Swift Testing — 手書き認識ロジックのテストを書く
- UIKit のモダナイゼーション —
UIFindInteractionによるシステム検索体験の統合
コメント
GitHub Issues · utterances