WWDC Quick Look 💓 By SwiftGGTeam
Add Live Text interaction to your app

Add Live Text interaction to your app

元の動画を見る

ハイライト

iOS 16 の VisionKit により、App は静止画像と一時停止動画フレームに Live Text を追加でき、ユーザーは文字選択、翻訳、住所や電話番号のデータ検出、QR コード操作ができます。


主要内容

一般的な画像ビューアは表示、ズーム、パンだけを担当します。画像に電話番号、住所、URL、QR コードがある場合、ユーザーは手動で書き写すか、システム写真ライブラリへ保存してから Live Text を使う必要があります。

この Session は iOS 16 と macOS 13 の VisionKit API を扱います。システム Live Text のインタラクション層を App に開放します。開発者が画像を提供し、ImageAnalyzer が非同期分析し、結果を ImageAnalysis として保存して ImageAnalysisInteraction または macOS の ImageAnalysisOverlayView に渡し、インタラクション層を表示します(01:33)。

デモは scroll view 内の画像ビューアです。Live Text 追加後、ユーザーは文字選択、コピー、検索、翻訳、電話番号タップで発信、住所タップでマップ起動、QR コード操作ができます(04:20)。

API の重点は制御です。.automatic でシステム既定動作を使うか、文字選択のみ、データ検出のみを有効化できます。下部の Live Text ボタンと Quick Actions も非表示、余白調整、カスタムフォントが可能です(05:26)。

詳細

1. 最小統合: 画像を分析し、結果を interaction に渡す(02:37

公式コードは統合を3オブジェクトに圧縮します。ImageAnalyzerImageAnalysisInteractionImageAnalyzer.Configuration です。画像変更時はまず古い分析結果を消し、新しい非同期分析を開始します。分析完了後、現在表示中の画像が入れ替わっていないか確認します。

import UIKit
import VisionKit

class LiveTextDemoController: BaseController, ImageAnalysisInteractionDelegate, UIGestureRecognizerDelegate {
   
    let analyzer = ImageAnalyzer()
    let interaction = ImageAnalysisInteraction()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        imageview.addInteraction(interaction)
    }
    
    override var image: UIImage? {
        didSet {
            interaction.preferredInteractionTypes = []
            interaction.analysis = nil
            analyzeCurrentImage()
        }
    }
    
    func analyzeCurrentImage() {
        if let image = image {
            Task {
               let configuration = ImageAnalyzer.Configuration([.text, .machineReadableCode])
                do {
                    let analysis = try await analyzer.analyze(image, configuration: configuration)
                    if let analysis = analysis, image == self.image {
                        interaction.analysis = analysis;
                        interaction.preferredInteractionTypes = .automatic
                    }
                }
                catch {
                    // Handle error…
                }
            }
        }
    }
}

キーポイント:

  • import VisionKit で Live Text フレームワークを導入します。
  • let analyzer = ImageAnalyzer() で画像分析器を作成します。Session はシステムリソース活用のため App 全体で1つの analyzer を共有することを推奨します(12:12)。
  • let interaction = ImageAnalysisInteraction() で iOS と macOS のインタラクションオブジェクトを作成します。
  • imageview.addInteraction(interaction) で画像を載せる view にインタラクション層を付けます。
  • 画像変更時、preferredInteractionTypes = []analysis = nil で古い画像のインタラクション状態をクリアします。
  • ImageAnalyzer.Configuration([.text, .machineReadableCode]) で文字と機械可読コードの両方を探すよう analyzer に指示します。
  • try await analyzer.analyze(image, configuration: configuration) で非同期に分析結果を生成します。
  • image == self.image は非同期分析完了時に画面が別画像に切り替わっていた場合の誤適用を防ぎます。
  • interaction.analysis = analysis で認識結果をインタラクション層へ渡します。
  • preferredInteractionTypes = .automatic でシステム既定の Live Text 動作を使います。

この例は古い状態を先に消す理由も説明します。分析には不確定な時間がかかります。分析中にユーザーが次の画像へ切り替える可能性があります。現在画像を確認しないと、古い分析結果が新しい画像を上書きする恐れがあります。

2. インタラクション種別: 文字選択とデータ検出のトレードオフ(05:26

preferredInteractionTypes はユーザーができることを決めます。.automatic は文字選択を提供し、Live Text ボタンが有効なときデータ検出項目に下線を付け、システム内蔵 App と同じ動作になります。

概念例:

// Conceptual Swift based on the session transcript.
interaction.preferredInteractionTypes = .automatic

キーポイント:

  • .automatic は Session が大多数の App に推す既定動作です。
  • テキストは選択、コピー、検索、翻訳できます。
  • Live Text ボタン有効後、住所、電話番号、URL などのデータ検出項目にワンタップ操作が付きます。

App が文字選択だけを望むなら .textSelection に変更します。このモードでは Live Text ボタン状態はインタラクション種別を変えません。

// Conceptual Swift based on the session transcript.
interaction.preferredInteractionTypes = .textSelection

キーポイント:

  • .textSelection は文字選択のみを残します。
  • Live Text ボタン状態に応じてデータ検出は自動表示されません。
  • 画像編集、注釈など誤タップを減らしたい画面に適します。

住所、電話、URL などの動作入口だけが必要なら .dataDetectors を使えます。Session では文字選択が無効になるため Live Text ボタンは表示されないと明言しています。

// Conceptual Swift based on the session transcript.
interaction.preferredInteractionTypes = .dataDetectors

キーポイント:

  • .dataDetectors は文字選択を無効化します。
  • 検出項目には直接下線が付きます。
  • ユーザーはワンタップで対応動作を起動できます。

もう1つの詳細: 文字選択または .automatic モードでは、長押しでデータ検出を起動できます。これは allowLongPressForDataDetectorsInTextMode で制御され、既定は有効です(06:21)。

// Conceptual Swift based on the session transcript.
interaction.allowLongPressForDataDetectorsInTextMode = false

キーポイント:

  • プロパティ名が作用範囲を示します。text mode での長押しデータ検出のみに影響します。
  • false にすると App 独自の長押しジェスチャーとの衝突を減らせます。
  • 文字選択自体はこの設定の影響を受けません。

3. 補助インターフェース: Live Text ボタンと Quick Actions の扱い(06:43

下部のシステム Live Text インターフェースは interaction が管理します。右下が Live Text ボタン、左下が Quick Actions です。Quick Actions は分析結果のデータ検出項目由来で、Live Text ボタンが有効なときのみ表示されます。

App 下部に独自ツールバーがある場合、補助インターフェースを隠して文字選択能力だけ残せます。

// Conceptual Swift based on the session transcript.
interaction.isSupplementaryInterfaceHidden = true

キーポイント:

  • isSupplementaryInterfaceHidden は Live Text ボタンと Quick Actions の表示を制御します。
  • true でも、有効なインタラクション種別の範囲内では画像内容を操作できます。
  • 下部 UI が密集した画像ブラウザに適します。

既存ボタンを隠したくないだけなら、余白を調整してシステム UI が App コンテンツを避けられます。

// Conceptual Swift based on the session transcript.
interaction.supplementaryInterfaceContentInsets = UIEdgeInsets(
    top: 0,
    left: 0,
    bottom: 44,
    right: 16
)

キーポイント:

  • supplementaryInterfaceContentInsets は VisionKit に避けるべき辺を伝えます。
  • 下部 inset で App 独自ツールバー用のスペースを確保できます。
  • 右側 inset でフローティングボタンを避けられます。
  • ここでの数値は概念例で、実プロジェクトはレイアウトから計算します。

App がカスタムフォントを使う場合、Live Text ボタンと Quick Actions に同じフォントを適用できます。Session はボタンサイズ一貫性のため Live Text は point size を無視すると注意します(07:52)。

// Conceptual Swift based on the session transcript.
interaction.supplementaryInterfaceFont = UIFont.preferredFont(forTextStyle: .body)

キーポイント:

  • supplementaryInterfaceFont は文字と記号のフォントウェイトに影響します。
  • point size はボタン寸法には使われません。
  • 統一したビジュアルスタイルが必要な画像ビューアに適します。

4. UIImageView 以外: contentsRect でハイライト領域を揃える(08:10

UIImageView は自身の contentMode で画像コンテンツ領域を自動計算できます。カスタム view にはこの便利さがありません。interaction の bounds が実際の画像表示領域より大きいと、文字ハイライトと画像内容がずれます。

修正は delegate メソッド contentsRectForInteraction を実装し、単位座標空間の画像コンテンツ矩形を返すことです。

// Conceptual Swift based on the session transcript.
func contentsRect(for interaction: ImageAnalysisInteraction) -> CGRect {
    return CGRect(x: 0.1, y: 0.2, width: 0.8, height: 0.6)
}

キーポイント:

  • contentsRect は view bounds に正規化した単位座標空間を使います。
  • 返す矩形は interaction bounds に対する画像コンテンツ位置を表します。
  • 例の数値は形状説明のみで、実プロジェクトは現在レイアウトから計算します。
  • interaction bounds が変わると VisionKit はこの rect を再取得します。

画像コンテンツ領域が変わっても interaction bounds が同じなら、VisionKit 更新を明示的に通知します。

// Conceptual Swift based on the session transcript.
interaction.setContentsRectNeedsUpdate()

キーポイント:

  • setContentsRectNeedsUpdate() は contents rect の再取得を要求します。
  • 画像トリミング、content mode、カスタムレイアウト変更に適します。
  • 画像の再分析は不要で、インタラクション層とコンテンツ領域の対応だけ更新します。

画像を scroll view に置く場合、Session は interaction を scroll view ではなく画像コンテンツを載せる view に置くよう明確に推奨します(09:18)。scroll view の可視領域とズームは常に変化し、contents rect の維持が難しくなります。

5. ジェスチャー衝突: App と Live Text がそれぞれのイベントを処理する(10:08

Live Text は複雑なジェスチャーを内蔵します。画像ブラウザ、注釈ツール、地図スクリーンショットビューアも独自のタップ、長押し、ドラッグを持つ場合があります。衝突時は2方向から対処します。

第一は、特定位置で Live Text がインタラクションを始めないようにすることです。Session は interaction(_:shouldBeginAt:for:) の実装を推奨します。指定点下にインタラクティブ項目がなく、かつアクティブな文字選択もなければ false を返し、Live Text に処理させません。

概念フロー: Live Text delegate がインタラクション開始を決定
1. interaction、タッチ point、interactionType を受け取る
2. point 下に Live Text インタラクティブ項目があるか確認
3. アクティブな文字選択があるか確認
4. 両方ない場合は false を返す
5. それ以外は true を返し Live Text を続行

キーポイント:

  • interaction(_:shouldBeginAt:for:) は Live Text ジェスチャー衝突の入口です。
  • 「point 下のインタラクティブ項目」は文字、リンク、電話、住所、QR コードなどの分析結果を含みます。
  • 「アクティブな文字選択」は空白タップで選択解除する体験を保つために使います。
  • false を返すと Live Text は対応動作を実行しません。

第二は、Live Text がイベントを必要とするとき App 側 gesture recognizer を譲ることです。Session の考え方は gestureRecognizerShouldBegin 内で同じ確認を行うことです。

概念フロー: App gesture recognizer が開始を決定
1. gestureRecognizer のタッチ point を読む
2. まず window 座標へ変換
3. 次に interaction がある view 座標へ変換
4. その点に Live Text インタラクティブ項目があれば false
5. アクティブな文字選択があれば false
6. それ以外は true で App ジェスチャーを続行

キーポイント:

  • まず window 座標、次に interaction view 座標へ変換すると scroll view ズーム場面で有効です。
  • Session は point が合わない場合、この座標変換を試すよう特に注意します。
  • false を返すと App ジェスチャーは開始せず、Live Text が引き継げます。

Session は UIViewhitTest(_:with:) をオーバーライドして同様の判断をし、ヒット位置でイベントを振り分ける方法にも言及します(11:46)。

6. 性能と動画フレーム: 変換を減らし、必要なものだけ分析する(12:00

ImageAnalyzer は Neural Engine を効率よく使いますが、UI のタイミングで呼ぶ必要があります。Session は3つのルールを示します。

第一、App 全体で可能な限り1つの ImageAnalyzer を共有します。第二、手元にある生の画像型を渡して形式変換を減らします。すでに CVPixelBuffer があるなら最も効率的な入力です。第三、画像が表示されようとしている、または表示されたときだけ分析します。タイムラインではスクロール停止後に分析を開始します(12:12)。

概念例:

// Conceptual Swift based on the session transcript.
final class LiveTextAnalyzerStore {
    static let sharedAnalyzer = ImageAnalyzer()
}

キーポイント:

  • static let は App 内で1つの analyzer を共有することを意味します。
  • 共有により複数ページで分析器を重複作成しません。
  • 実プロジェクトではページライフサイクルに合わせ不要な分析タスクをキャンセルも必要です。

動画には2つの入口があります。AVPlayerView または AVPlayerViewController では、iOS 16 は allowsVideoFrameAnalysis で既定の一時停止フレーム Live Text を提供し、FairPlay 保護コンテンツ以外に適用されます(13:12)。

// Conceptual Swift based on the session transcript.
playerViewController.allowsVideoFrameAnalysis = true

キーポイント:

  • allowsVideoFrameAnalysis は AVKit の一時停止フレーム分析を制御します。
  • Session では既定で有効と述べています。
  • FairPlay 保護コンテンツはこの能力をサポートしません。

AVPlayerLayer を使う場合、App は分析と interaction を自分で管理します。鍵は現在フレームを取る currentlyDisplayedPixelBuffer です。Session はこれが正しいフレームを分析する唯一の方法だと明言します。再生レートがゼロのときのみ有効値を返し、shallow copy で書き込み不可です(13:32)。

// Conceptual Swift based on the session transcript.
if player.rate == 0,
   let pixelBuffer = playerLayer.currentlyDisplayedPixelBuffer {
    Task {
        let configuration = ImageAnalyzer.Configuration([.text, .machineReadableCode])
        let analysis = try await analyzer.analyze(pixelBuffer, configuration: configuration)
        interaction.analysis = analysis
        interaction.preferredInteractionTypes = .automatic
    }
}

キーポイント:

  • player.rate == 0 は一時停止フレーム場面に対応します。
  • currentlyDisplayedPixelBuffer は画面上に表示中の動画フレームを読みます。
  • analyze(pixelBuffer, configuration:) は動画フレームを別画像型へ先に変換する必要を避けます。
  • interaction.analysis = analysis で分析結果をインタラクション層へ渡します。
  • 例は完全なエラー処理と状態確認を示していません。実プロジェクトは静止画例と同様に非同期完了後の状態変化を扱う必要があります。

重要ポイント

  1. 何をするか: 画像メモ App に「画像内文字を選択してコピー」機能を追加する。 なぜ価値があるか: Live Text が OCR、選択ジェスチャー、コピーメニューを担当し、App が独自 OCR インタラクション層を書く必要がありません。 始め方: 画像 view に ImageAnalysisInteraction を追加し、ImageAnalyzer.Configuration([.text]) で現在画像を分析します。

  2. 何をするか: レシート、名刺、ポスター画像にワンタップ発信、マップ起動、URL アクセスを追加する。 なぜ価値があるか: .automatic.dataDetectors が電話番号、住所、URL をタップ可能な操作に変えます。 始め方: preferredInteractionTypes.automatic に設定。画面が動作入口だけ必要なら .dataDetectors に変更します。

  3. 何をするか: 画像ビューアでカスタム下部ツールバーを保ちつつ Live Text を有効化する。 なぜ価値があるか: supplementary interface は非表示にでき、content insets で既存 UI を避けられます。 始め方: まず interaction を統合し、UI 密度に応じて isSupplementaryInterfaceHidden または supplementaryInterfaceContentInsets を選びます。

  4. 何をするか: 動画プレイヤーに一時停止フレームの文字と QR コード認識を追加する。 なぜ価値があるか: AVKit は AVPlayerViewAVPlayerViewController に既定の一時停止フレーム分析を提供。カスタム AVPlayerLayer も現在 pixel buffer で同じ分析フローに接続できます。 始め方: AVKit では allowsVideoFrameAnalysis を確認。カスタム layer では一時停止時に currentlyDisplayedPixelBuffer を読み ImageAnalyzer へ渡します。

  5. 何をするか: ズームとジェスチャーがある画像注釈 App に制御可能な Live Text を追加する。 なぜ価値があるか: delegate と gesture recognizer delegate で文字選択、注釈ドラッグ、空白タップによる選択解除を分離できます。 始め方: まず image view に interaction を置き、interactionShouldBeginAtPointgestureRecognizerShouldBegin で衝突点を処理します。


関連セッション

  • Capture machine-readable codes and text with VisionKitDataScannerViewController でライブカメラ内の文字と機械可読コードを処理し、本 Session の静止画像・一時停止動画フレームと補完関係にあります。
  • What’s new in Vision — Vision の文字認識、バーコード検出、モデル revision 更新を説明し、Live Text 背後の認識能力の理解に役立ちます。
  • Create a great video playback experience — iOS と iPadOS のシステムメディアプレイヤー更新を紹介し、動画 Live Text と一時停止フレーム再生体験を扱います。

コメント

GitHub Issues · utterances