投影の動作と高度な組み込み
最初にCapacitorまたはCordovaのガイドを確認してください。このリファレンスでは、共通のDOM要件、投影先の規則、既存レンダラー向けの低レベルAPIを説明します。
責務
| Engine | 利用側プラグイン |
|---|---|
| DOM計測、画像のジオメトリ、対応するネイティブスクロールバインディング | ネイティブビューの作成、接続、配置、サイズ設定 |
| 起動、イベント購読、ネイティブ描画の確認 | 適切なレイヤーへのフォールバックDOMの用意 |
| 準備済み画像のキャッシュ、デコード、表示、撤去 | 外観の変更と意図した投影先の通知 |
| ネイティブキャプチャと一時的な復帰画像 | ネイティブビューの提供とSDKのキャプチャ適格性 |
| 画像の無効化、トークンの解放、オブザーバーの終了処理 | 業務アクションと操作可能なWebフォールバックの管理 |
DOMスロットに必ずWebの代替表示があるとは限りません。UIプラグインはボタンを復元できる一方、広告プラグインはスロットを非表示にする場合があります。画像の受け渡しは明示的で、画像は操作できません。Engineは画像を通してクリックを転送せず、SDKのクリック・インプレッション処理を置き換えません。
投影の契約
通知が指定するのはサーフェスの配置先であり、キャプチャ方法ではありません。プラグインが投影状態の別コピーを保持しなくても、Engineがプラットフォームに応じた切り替えを選択します。
| 意図した投影先 | 必要な動作 |
|---|---|
native |
確認済みのジオメトリで操作可能なネイティブビューを表示します。移動中は、対応するネイティブ追従または準備済み画像を使います。 |
web |
ネイティブ投影を取り除いて、覆いかぶさるWebレイヤーより下にネイティブコンテンツを保ちます。準備済み画像またはプラグインのWebフォールバックを使います。 |
none |
ネイティブコンテンツ、投影画像、フォールバックコンテンツを撤去します。後から来る更新・キャプチャのコールバックで再表示してはいけません。 |
オーバーレイの被覆は一時的にnativeをwebに変えることがありますが、noneを上書きしません。キャプチャ失敗で意図した投影先が変わることもありません。failedFallback: nativeが有効なのは、利用側が自身でネイティブ配置を維持する場合だけで、明示的なweb、none、オーバーレイの被覆を上書きしません。
画像の準備完了と復元の確認はEngineが管理します。代替のピクセルが用意される前にネイティブのピクセルを隠さず、ネイティブの復元が成功する前に代替のピクセルを撤去しません。位置だけの移動では準備済みコンテンツを再利用し、外観、サイズ、レンダラーの識別情報の変更では無効化します。画像がない場合は、遷移中のキャプチャを待たず、宣言されたフォールバックを直ちに使います。
登録と通知転送は起動・再接続をまたいで最新の意図を保持し、業務イベントは再実行しません。破棄時には登録とキャッシュ画像を撤去します。通知は意図を表すもので、完了を保証するPromiseではありません。非表示の完了を保証するAPIは、DOMとネイティブサーフェスの要件で説明する明示的な撤去確認を待つ必要があります。
CSSの表示状態を調べる既存レンダラーは、npmパッケージのgetUnprojectedVisibility(element)を呼べます。Engineの非表示ルールを除いた、現在の指定・継承された表示状態を読み、戻る前に投影の所有権を復元します。利用側プラグインからEngineのマーカー属性を検査・変更しないでください。このヘルパーはスタイルを読み取り、属性オブザーバーを発火させる場合があります。計測や通信のコストがなくなるわけではありません。
DOMとネイティブサーフェスの要件
projectionSelectorは必須で、投影開始時に既存のコンテナー1つへ解決される必要があります。プラグイン自身のquerySelectorで取得するコンテナーも対象です。EngineはDOMを作らず、サイズ、配置、レイヤー、z-indexも選びません。コンテナーの通常のレイアウトと操作可能なWebコントロールを維持してください。空でサイズがゼロのプレースホルダーは、利用可能なフォールバックではありません。これは固定スコープであり、探索のルートではありません。
登録時点でコンテナーが存在する必要はありません。DOMがない場合はネイティブコンテンツを表示せずに待ち、置き換わると新しいバインディングを作成して古い画像を撤去します。Engineはコンテナーの子要素、リスナー、値を保持します。viewにはネイティブビュー自体を渡してください。コントロールのグループには共通のネイティブコンテナーを使えます。内部識別子はEngineが管理するため、IDリストやビュー辞書は不要です。独立したサーフェスには別々の登録を使います。
projectionSelector: 'native-ad[placement="home"]'のように、Web Componentのホストを投影先にできます。ホストにレイアウトボックス(display: blockと意図した寸法)を与えてください。Engineは挿入、削除、セレクターに関係する属性変更を監視します。セレクターはドキュメント内で実行され、shadow root内は検索しません。shadow内を含め、フォールバックコンテンツはホストのvisibilityを継承する必要があります。visibility: visibleで上書きせず、ホストの::beforeは投影画像のために予約してください。これらの要件はopen・closed両方のshadow rootに適用されます。
独立して動くAdMobスロットでは、読み込み済みネイティブビュー・サーフェスごとに登録を1つ保持し、スロット固有のセレクターを使って、receiveListeners.matchで通知を絞り込みます(match: ["slotKey": slotKey, "loadId": loadId] / match = mapOf("slotKey" to slotKey, "loadId" to loadId))。ホストビューを再利用する場合でも、読み込んだ広告を置き換える際は登録を解放します。SDKの読み込み、インプレッション、クリックの処理は広告プラグイン側に残してください。既存のキャプチャ適格性チェック(テスト広告・不透明背景・動画なしなどの制約)をcanCaptureに渡します。EngineはSDKビューのスナップショット取得を許可するものではありません。
同等のWebフォールバックがないSDKの利用側では、iOSでfailedFallback: .native、AndroidでfailedFallback = NativeUIProjection.FailedFallback.NATIVEを設定します。既定値は.web / WEBで、既存の利用側の動作を維持します。自動的な移動処理で準備済み画像を使えない場合(未準備、キャプチャ非対応、切り替え拒否)、nativeは実際のネイティブビューを保持し、フォールバックDOMを非表示のままにします。そのビューの配置は利用側が継続して管理する必要があります。このオプションはAndroidにネイティブスクロール追従を追加しません。明示的なweb、none、オーバーレイの被覆が優先されます。意図的に覆いかぶさるWebレイヤーより上にネイティブビューを持ち上げることはありません。
不透明でソフトウェア描画可能なAndroid SDKビューには、captureMode = NativeUIProjection.CaptureMode.VIEWと明示的なfailedFallback方針を組み合わせます(利用側がネイティブ配置を管理しない限りWEB)。周囲の余白を含めずにViewのピクセルだけを描くため、一部だけ表示される広告をクリップしても、ツールバーや隣のコンテンツが焼き込まれません。動画、SurfaceView、半透明ビュー、Window合成を必要とするコンテンツは、既定のCOMPOSITEDモードを維持してください。利用側は、不透明なView全体に対応し、クリップされたDOMホストを用意する必要があります。任意のネイティブ輪郭マスク、変形、独自クリッピングは再現されません。
VIEWのピクセルは、ネイティブViewの識別情報、寸法、コンテンツリビジョンをキーにキャッシュします。通常のスクロールではデコード済みWeb画像を維持し、キャプチャ、PNGエンコード、画像デコードをせず配置だけを更新します。画像はネイティブ配置の確認遅延とは独立して、ローカル原点(0, 0)でホストのborder boxを覆います。スクロールとスクロールの間も、不透明画像は実際のネイティブViewの下に描画されたままです。そのViewを隠す前に、EngineはWebViewの表示準備と描画を待ちます。VIEWはネイティブの進入用・復帰用画像コピーを作りません。復帰の確認は、ネイティブ描画とその次のアニメーションフレームをまたいで待ちます。世代チェックにより、遅れた被覆コールバックが復元・一時停止後にコンテンツを隠すことを防ぎます。
VIEWモードでは、PNGエンコード後にネイティブBitmapを再利用可能な状態へ解放し、PNGとデコード済みWeb画像はキャッシュに残します。プラグインの更新通知はコンテンツリビジョンを進めるため、Viewを置き換えずに内容を変える際も必ず更新を通知してください。登録の置き換え、WebViewのナビゲーション、reset、closeでキャッシュを撤去します。エンコードはUIスレッド外で行い、Viewの描画とキャッシュの所有権はUIスレッド上で管理します。既存の800万ピクセル上限も維持します。
COMPOSITEDは半透明コンテンツのため、ネイティブの受け渡し用コピーを保持します。進入用コピーはWebViewの表示準備と描画を待ってから撤去します。500msの進入期限が切れた場合は、コピーを除去する前にネイティブViewを復元し、遅れた描画確認は拒否します。
Android VIEWの利用側は、被覆成功後にUIスレッドでisProjectingを読み、同サイズのネイティブ配置処理を延期できます。最後に受け付けた配置だけを保持し、シーケンス・世代の検証を続け、非表示、リサイズ、終了処理は直ちに処理してください。通常の復元、失敗からの復旧、使用中画像の解放によってネイティブのピクセルを再表示する前に、保留した配置を同期適用するbeforeNativeRevealを渡します。Engineはコールバックの前にisProjectingを解除するため、配置の適用を再び延期してはいけません。コールバックは保留中の処理がなくても呼ばれる場合があり、冪等である必要があります。非表示・破棄時には利用側の保留処理を消去し、適用前に読み込み済みViewの識別情報を検証してください。このコールバックは、コンテンツ・サイズの無効化やJavaScriptのジオメトリ追跡を置き換えません。
Androidで、非同期hideメソッドが全ピクセルの消失を保証する場合は、projection.hide { completed -> ... }を呼び、completedがtrueの場合だけ成功として完了させます。ネイティブの受け渡し用コピーは直ちに消えますが、完了はWebプロキシの同期的な撤去を待ちます。タイムアウトは成功ではなく失敗です。再表示時には通常のプラグインイベントに加えてprojection.show()を呼んでください。これにより、イベント購読の開始中でも表示の意図を保持します。
スクロール監視は、割り当てられたslotを含むcomposed treeの祖先をたどります。Ionicのshadow tree内の非composedなscrollイベントも発生元で監視するため、アプリでリスナーを追加したり、擬似的なdocumentイベントを送信したりする必要はありません。リスナーはホストの親変更やslotの再割り当てに追従し、終了時に削除されます。
スクロールは内部で処理します。iOSでは一意に対応付けられるネイティブスクロールバインディングがあれば使用し、それ以外(Androidを含む)ではスクロール中だけ準備済み画像を使い、動きが落ち着いてからネイティブコンテンツを復元します。自動スクロール処理は明示的なwebやnoneを上書きしません。キャプチャやジオメトリが非対応なら通常のDOMフォールバックを維持し、遷移経路で新しいキャプチャを待ちません。最後の明示的な投影先はWebView再読み込み後も維持し、古いドキュメントへの応答で新しいドキュメントのリクエストが完了することはありません。
既定では、内部チャネルはCapacitorのlocalhostオリジンのメインフレームに限定されます。別のオリジンを設定する場合は、アプリの正確なallowedOriginsを渡してください。AndroidにはWebViewのWEB_MESSAGE_LISTENERとDOCUMENT_START_SCRIPT機能が必要で、非対応のWebView実装では登録が明示的に失敗します。リモートへナビゲーション可能なWebViewに、ワイルドカードオリジンを使わないでください。
Ionicオーバーレイ対応(任意)
Ionicアプリでは、起動時に次を1回呼びます。アプリから直接呼ぶ場合は、@webview-native-ui/engineをアプリの直接依存としてインストールしてください。
import { enableSupportIonicOverlays } from '@webview-native-ui/engine/ionic';
enableSupportIonicOverlays();
プラグインのメンテナーは、アプリ設定の説明にこのコードを含めるか、既存のJS初期化処理内で呼べます。@rdlabo/capacitor-native-uiはすでに同じ初期化処理を内部で呼んでいるため、その利用者に追加呼び出しは不要です。Ionic以外のアプリでは省略します。ネイティブ側の登録だけではIonicのイベントリスナーはインストールされません。
npmモジュールとSwift・Androidから注入されたランタイムの間も含め、モニターはドキュメントごとに1つ共有します。繰り返し初期化してもリスナーは追加されず、明示的な設定も消えません。投影を1つ破棄しても、その購読だけを削除し、他のプラグインが使うモニターは停止しません。モニターの生存期間はドキュメントと同じです。
既定ではModal、Popover、Alert、Action Sheet、Loading、Picker、Toast、Menuのライフサイクルイベントを扱います。各投影のDOMをアクティブなオーバーレイのスタックと比較し、最前面モーダル内のコンテンツはネイティブ表示のまま、覆われたコンテンツは一時的に準備済み画像を使います。Toastや非アクティブなsheetのバックドロップはサーフェスを覆いません。オーバーレイがネストしている場合は、残るスタックが復元を許すまで被覆を維持します。画像がなければ通常のWebフォールバックを維持します。この一時的な被覆は、プラグインの明示的なweb・noneを上書きしません。後から登録された投影も現在の状態を読み取ります。
ネイティブPopoverの所有権はNative UIのvertical-barsレンダラーが提供します。共通モニターはそのソースDOMを、覆いかぶさるWebオーバーレイとして扱いません。ネイティブ表示に失敗した場合は、レンダラーが所有権を解放し、通常のDOMによる被覆に戻ります。
任意の設定はドキュメント全体に適用されます。
enableSupportIonicOverlays({
exclude: '[data-nonblocking-overlay]',
additional: [
{
selector: 'app-dialog',
events: {
willPresent: 'appDialogWillPresent',
didDismiss: 'appDialogDidDismiss',
},
},
],
});
登録済みのDOM投影に独自の被覆ルールが必要な場合だけ、covers(overlay, target)を使います。既定の動作にはundefinedを返し、上書きする場合はbooleanを返してください。除外と明示的なネイティブウィンドウの所有権が優先されます。標準の関係判定は、任意のCSSスタッキングコンテキストを解釈しようとせず、オーバーレイの順序、DOMの包含関係、バックドロップの状態を使います。特殊なレイアウトは明示的に設定してください。
低レベルのWeb投影
スコープは、生存期間全体で1つの既存Webフォールバック要素にバインドします。その要素はプラグインのDOMから取得します。Engineは代替ホストを作成せず、ページ、重なり順、z-indexを選びません。別のフォールバック要素には新しいスコープが必要です。Documentはelement.ownerDocumentから取得します。
import { createWebProjectionScope, createWebProjection } from '@webview-native-ui/engine';
const fallback = container.querySelector<HTMLElement>('[data-web-fallback]');
if (!fallback) throw new Error('Web fallback is missing');
const scope = createWebProjectionScope(fallback, 'my-plugin');
const projection = await createWebProjection(
scope,
{
send: (request) => plugin.snapshot(request),
listen: (listener) => plugin.addListener('snapshotInteraction', listener),
},
{
wake: () => schedulePluginUpdate(),
blocked: () => overlayIsActive(),
revision: () => lastSentRevision,
},
);
上記のコンテナー、ブリッジ、更新コールバックは利用側プラグインのものです。投影マネージャーにdocument、target、layer、CSSセレクター、レイヤー解決関数は渡しません。
- ネイティブ更新が受け付けられた後で、
projection.consider(revision, sources)を呼びます。sourcesはネイティブIDとビューポート内のフレームを元のDOMへ関連付けます。フォールバック要素の内側にある必要はありません。画像は事前にキャプチャ・デコードされます。 - 外観変更には
projection.changed()、スクロールジオメトリの無効化にはprojection.changed(scrollElement)を呼びます。レイアウト更新にはconsiderを使い、その他のジオメトリ変更にはinvalidate()を使います。 - WebオーバーレイでNativeを覆う前に
projection.begin()を呼びます。固定のフォールバックに準備済みピクセルを表示し、この時点で新しいキャプチャやデコードは始めません。キャプチャが利用できなければ、プラグインは通常のWebフォールバックを維持します。 - 画像がソースを所有している間は、通常のNative更新から
projection.ownersを除外します。visibleOwnersはNative復元中の復帰用ピクセルを維持し、preparedOwnersは準備済み画像のあるソースを示します。これらの配列は読み取り専用のスナップショットです。 projection.end()を呼び、通常のネイティブ更新を送信した後、projection.updated(snapshotToken, acceptedIDs, revision)を呼びます。キャプチャしたすべてのIDを含む、より新しい確認応答によって画像を撤去します。- サーフェスの解放時に
projection.destroy()を呼びます。自分の画像とリスナーだけを解放します。最終的なNativeのリセットとDOMはプラグインが管理します。
Engineは準備済み画像に、操作できない空のCSS ::before疑似要素を使います。既存のdiv、button、カスタム要素など、疑似要素に対応するコンテナーを用意してください。img、input、videoのような置換要素・void要素はスコープ作成時に拒否されるため、既存のフォールバックコンテナーを使います。既存のフォールバックや子要素を複製・置換・削除したり、inert・aria-hiddenを設定したりしません。一時的な画像スタイルを除去すると、投影中のアプリ側の変更を含めて、同じ生きたDOMを復元します。イベントリスナーとフォーム値も保持します。レンダラーは同じ要素を別の親へ移動できますが、スコープの要素自体を置き換えたり、2つのマネージャーで同時に制御したりしてはいけません。
既存プラグインからの通知
低レベルマネージャーは、options.eventsを通じて既存プラグインのイベントを購読できます。
events: {
addListener: (name, listener) => plugin.addListener(name, listener),
update: ['setColors', 'setTabs', 'tabSelected'],
projection: {
native: ['shown'],
web: ['transitionStarted'],
none: ['hidden'],
},
}
これらは通知であり、横取りするメソッド名ではありません。プラグインはネイティブ変更の成功後に更新イベントを送信します。Web投影イベントは移動開始前に届く必要があり、事前に準備した画像を使います。nativeイベントは復元を要求しますが、通常のレンダラーの確認応答でNativeの準備完了が確認されるまで画像を維持します。hiddenイベントはプラグインのネイティブ非表示処理に続いて画像を撤去します。hide()を呼んだり、アプリが所有するフォールバックDOMを変更したりしません。外観更新はnoneを含む要求済み投影先を維持します。
複数のサーフェスを持つプラグインでは、accepts(event)でこのスコープの安定したサーフェス識別情報を選択します。updateと投影先で共有するイベントは1回だけ購読し、まとめて処理します。同じイベントに矛盾する投影先を設定すると、購読前に拒否します。マネージャーを破棄すると、初期化失敗後に遅れて完了した登録を含め、すべての購読を解除します。公開プラグインメソッドをラップしたり再実行したりしません。
マネージャーは、非公開のネイティブファイルURLをCapacitorで内部変換します。プラグイン開発者がURL解決関数を渡す必要はありません。
この節では、Ionicアダプターのように、すでにバッチ単位の状態整合を管理しているレンダラー向けの低レベルマネージャーを説明します。新規の組み込みでは、起動、計測、描画確認を管理する、プラグインガイドのネイティブ登録を優先してください。
1つのNativeトランザクション内の複数フォールバック
複数のフォールバック要素を同時に切り替える必要がある場合は、createWebProjectionBatch(transport, options)を使います。同じ画像エンジンを使い、リスナーは1つ、キャプチャ・被覆・復元のトークンも1つです。各エントリーの{ scope, element, control, ... }をbatch.consider(revision, sources)へ渡します。ボタングループのように、複数のネイティブIDで1つのスコープを共有できます。
バッチは最初の空でないconsiderからDocumentを取得します。以後のスコープとソースはすべてそのDocumentに属する必要があります。最初の更新前は、begin()は何もせず、destroy()も安全です。フォールバックが変われば新しいスコープが必要です。スコープの識別情報はキャッシュの無効化に関与し、表示中は復元が終わるまでキャプチャ時のスコープを保持します。
WebProjectionRequestはactionを判別子とするunionです。prepareにはrevision、viewportWidth、ids、coverにはtoken、restoreにはtoken、viewportWidth、投影先のparts、painted・restored・releaseにはtokenが必要です。resetはペイロードを取りません。準備結果が空、または{ shown: false }の場合、プラグインは通常のWebフォールバックを使います。プラグインの既存の順序付きブリッジを使ってください。Engineはユーザー操作を再試行しません。
両プラットフォームのネイティブ切り替え
ビューとスナップショットのライフサイクルAPIは、すべてメイン・UIスレッドで呼びます。ネイティブ入力のフレームは、iOSではWebViewを基準とするポイント、Androidでは参照ビューを基準とする物理ピクセルです。viewportWidthはCSSピクセル単位のWebビューポート幅です。JavaScriptへ渡す準備済みパーツはCSSピクセルへ変換します。
両プラットフォームとも、coverは同じUI処理の中で、渡された実際のビューを隠す前に準備済みの進入画像を配置します。booleanの戻り値は実行できたかを示し、失敗時は表示状態を変えません。キャプチャする各コンポーネントの実際のビューを渡してください。別途ビューを隠したり、別の進入用コピーを作ったりしないでください。
let shown = snapshots.cover(token, in: webView, views: renderedViews, transient: movingSurface)
val shown = snapshots.cover(token, activity.window, webView, renderedViews, transient = movingSurface)
transient: trueは移動する前景サーフェス用で、背景のcoveringTokenを置き換えません。Web画像の準備完了後にretireCopy(token)で進入用コピーを撤去します。通常のネイティブ更新を適用する前に、iOSのrestoreControls()・AndroidのrestoreViews()でEngineの表示状態変更を元に戻してください。復帰時にはrestoreがキャッシュ済み復帰画像を配置し、プラグインがその下を描画し、対応する確認応答・準備完了後にretireCopy(token, entry: false)で撤去します。名前は各プラットフォームの慣習に沿っていますが、ライフサイクルは同じです。coverは進入時の操作であり、通常の更新ごとに繰り返すメソッドではありません。
トークンの解放にはrelease(token)、所有者の終了にはreset()(Androidでエンコーダーを解放する場合はclose()も)を使います。サーフェスマネージャーは他のサーフェスのキャプチャをリセットしてはいけません。キャプチャやネイティブ配置が利用できない場合、Webフォールバックは引き続きレンダラーの責務です。
任意のeligible(source)とcurrent(sources)コールバックにより、フレームワーク固有のサーフェス選択をEngineの外側に保ちます。transient: trueは、復帰画像の基準位置を更新する必要がある移動サーフェスに対応します。ネイティブスクロール追従は任意のCSS変形には対応しないため、そのような移動ではこの画像経路を使えます。
画像通信はWebProjectionRequestを受け取り、準備済みトークン・パーツまたは{ shown: false }を返します。パーツはローカルのfile: URLとCSSピクセルのビューポート座標を使います。リクエストにはプラグインの既存の順序付きブリッジを使います。
| Action | ネイティブ側の責務 |
|---|---|
prepare |
現在のリビジョンとIDを検証し、実際に描画されたフレームでNativeUICoveredSnapshots.prepareを実行します。 |
cover |
トークンとネイティブサーフェス全体の所有権を検証し、進入用コピーを作成して関連するネイティブビューを隠します。失敗時はshown: falseを返します。 |
painted |
そのトークンの進入用コピーだけを撤去します。 |
restore |
渡されたフレームに準備済み復帰コピーの基準位置を合わせます。ここでキャプチャは行いません。 |
restored |
ネイティブ更新のACKとWeb描画の境界の後、そのトークンの復帰用コピーだけを撤去します。 |
release |
指定トークン、ファイル、関連する一時ビューを解放します。 |
reset |
所有者の終了処理のみで使用し、ネイティブキャプチャの全リソースを解放します。 |
操作の開始・終了をJSリスナーへ通知するにはNativeUICoveredSnapshots.observeを使います。キャプチャは合成済みピクセルを使うため、ぼかし・屈折を保持し、クリップされたネイティブコンポーネントの下の背景も含みます。そのためキャッシュの有効性には、レイアウトとプラグインの表示変更通知も関係します。Engineは常時キャプチャを行わず、あらゆるレンダラーのキャプチャを保証もしません。
一時ストレージとアプリの権限
準備済み画像はアプリ専用の一時ストレージに保持します。iOSはFileManager.default.temporaryDirectoryを使います。Androidではcontext.cacheDir内のディレクトリでNativeUISnapshotsを構築し、共有・外部ストレージを使わないでください。この経路では、どちらのプラットフォームも写真ライブラリやストレージの権限ダイアログは不要です。Capacitor Filesystemプラグインも必要ありません。AppleのサンドボックスディレクトリとAndroidのアプリ固有ストレージを参照してください。
Capacitor.convertFileSrcは返されたファイルURLを既存のWebViewローカルサーバーへマッピングします。ファイルシステムの権限を付与せず、画像をアップロードしません。アプリでContent Security Policyを設定する場合、img-srcはそのURLが使うローカルアプリオリジンを許可する必要があります。広範なfile:アクセスや無関係なWebViewファイルアクセス設定を有効にしないでください。
release(token)と所有者の終了処理は、自分のスナップショットファイルを削除します。プロセスが突然終了すると、OSがキャッシュを消すかアプリがアンインストールされるまで一時画像が残る場合があり、クラッシュ直後の削除は保証されません。ストレージを非公開に保ち、永続的なアプリデータではなく使い捨てとして扱ってください。
ネイティブスクロール追従
readScrollPlacement(element, owner, identify)は、変形のないoverflow所有者1つを計測します。createScrollBindings()は再利用の妥当性を確認し、取得がACKを待つ間の移動を検出します。reuseはレイアウトではなく確認応答の状態を表すため、レイアウト重複排除のシグネチャに含めないでください。
iOSでは、NativeUIScrollBindings.replaceが所有者ごとにソースをまとめ、一意に対応するUIScrollViewにだけバインドします。update()はコンテンツを再構築せず、位置とクリッピングを適用します。NativeUIClippedViewは表示ピクセルとヒットテストを制限しつつ、コンテンツ全体の寸法を保持します。
確立済みのバインディングがネイティブ側で拒否されると所有権を失い、同じ更新・ACK経路から新たに取得できます。新規取得に失敗した場合は、関連するレイアウト・スクロール終了イベントまでフォールバックを維持します。即時の再試行ループやユーザー操作の再実行を追加してはいけません。フレーム・プロパティの変更で、確立済みの座標原点を再調整することはありません。
これは非公開クラス名を使わず、WKWebView内部のoverflow階層を利用します。ビューがない・曖昧な場合、ネストしたスクロール、ドキュメントスクロール、非対応の変形、形状を指定したクリッピング、未確認で移動中の所有者にはバインドできません。UIScrollViewが存在するだけで対応しているとは判断しないでください。ネイティブビューはその内部で始まるジェスチャーを受け取る場合があり、ジェスチャーの転送とSDKのアクセシビリティ動作はレンダラーごとの対応になります。
Androidライブラリ
Androidは、Capacitorプラグイン登録ではなく、Mavenを通じてAARとして配布するGradle Androidライブラリを使います。公開座標はio.github.webview-native-ui:engine:<version>です。ローカルのMaven公開物をビルド・検証します。
android/gradlew -p android testDebugUnitTest assembleRelease connectedDebugAndroidTest publishReleasePublicationToVerificationRepository
NativeUIClippedViewは、変化していない寸法をリセットせず、呼び出し側から渡された位置とクリッピングを適用する低レベルヘルパーです。NativeUIProjectionは画像の受け渡しと復元を管理し、このヘルパーでレンダラーをレイアウトするわけではありません。通常のネイティブ配置とサイズ更新は利用側プラグインが管理します。AndroidではWKWebView形式のネイティブoverflowバインディングへの対応を表明していません。高レベル登録は、タッチを消費しないウィンドウのタッチオブザーバーを共有します。downは進行中のキャプチャ採用を中止し、up・cancel・フォーカス喪失は準備を再び許可し、終了時はその登録だけを削除します。既存のコールバック送信と戻り値は維持されます。
NativeUISnapshotsは合成済みのPixelCopy画像とローカルPNGファイルを準備し、そのキャッシュから被覆・復元します。エンコードはUIスレッド外で行い、所有者ごとに準備処理を1つだけ実行します。表示変更と操作を通知し、終了時にトークンを解放してください。ビュー・ライフサイクルの呼び出しはすべてUIスレッド上で行います。リビジョン、適格性、フォールバック方針はプラグインが保持します。
準備済みWebピクセルの表示・撤去後、NativeUIWebReadiness.prepareは続くWebView描画の準備状況を確認します。current述語では、所有セッション、ソースの世代、画像リビジョンを検証する必要があります。一時的なネイティブコピーがこの非同期の受け渡しを覆い、オーバーレイ自体の開始は待ちません。ブリッジの確認応答もこのコールバックも、物理ディスプレイへの表示完了を証明するものではありません。
AdMob PR #487は、不透明なテスト広告の下に画像を持続的に置く例です。この方針は全体で有効にはなっていません。半透明UIは二重に合成され、動画・SDKの操作にはレンダラー固有の判断が必要なためです。画像を通してクリックを合成してはいけません。Native UIの利用側はiOSとAndroidの両方でEngineを使います。Androidの受け入れテストは、実際のCapacitorブリッジと半透明のComposeコンテンツを扱います。他のSDKの利用側にも、独自の組み込み・受け入れテストが必要です。
半透明のAndroidサーフェス
半透明ビューを空のBitmapへ描く代わりに、背景を含む合成済みウィンドウのピクセルをキャプチャします。準備済みWeb画像はデコード済みで非表示のまま保持します。切り替え時には、cover(token, window, reference, views)が同じUI処理内で、ネイティブビューを隠す前に一時的な不透明画像コピーを配置します。Web画像を表示し、Web描画の準備完了を確認してから、進入用コピーを撤去します。オーバーレイは直ちに開始し、この経路で画像エンコードは行いません。
復帰時にはrestoreで準備済み復帰コピーを配置し、その下のネイティブビューを復元してWeb画像を隠し、Web描画の準備完了後に復帰コピーを撤去します。これにより、実際の半透明ネイティブピクセルを自身のスナップショットの上へ合成することを避けます。すべての非同期コールバックで、トークンと現在のサーフェス世代を検証してください。一時コピーは操作不可で範囲が限定されており、終了時に両方のコピーとキャッシュリソースを解放します。
保持するのはキャプチャ時の背景であり、画像の背後で変化し続ける光景ではありません。背景が変わったら、コンテンツ更新と同じ表示変更通知でキャッシュを無効化する必要があります。動画と非対応のキャプチャサーフェスは、利用側のフォールバック方針を維持します。
利用側の組み込みテストは、それぞれのリポジトリに置きます。EngineのCIは、隣接するレンダラーのチェックアウトを必要とせず、ブラウザー、Swift、Androidの契約を検証します。
不透明画像から戻るときの最終配置(Android)
通常、スクロールごとにネイティブ配置を計測・送信するプラグインは、最終配置の受け渡しを選択できます。AndroidのNativeUIProjection登録にupdatePlacement: (JSONObject) -> Unitを渡し、WebホストをbindProjectionPlacement(element, measure, invalidate)でバインドします。
binding.projectedがtrueになるのは、ネイティブ側が画像の受け渡しを確認した後だけです。trueの間は、プラグインはそのホストの最後の配置を全体の配置バッチで再利用し、計測を省略できます。他のホストは通常の更新を続けます。measure()はJSONオブジェクトを同期的に返します。Engineはそれを既存の復元リクエスト(または使用中画像の復旧リクエスト)に含めます。ネイティブ側は使用中画像のトークンとViewの識別情報を検証し、updatePlacementを呼んでからネイティブを表示し、描画を確認します。ブリッジの往復は追加されません。- プラグインは
updatePlacementで自分の識別情報、世代、ホストごとのシーケンスを検証する必要があります。古い・無効な配置を拒否するには例外を投げます。あるホストの最終更新によって、他のホストの更新が破棄されてはいけません。 invalidate()は切り替えリクエストが失敗した後に呼ばれます。楽観的な配置・重複排除キャッシュを消去し、通常の配置整合処理を予約します。- ホストを取り外す際はバインディングを解放します。セッションの所有権により、古い登録への遅れた確認応答が新しい登録の投影状態を消すことを防ぎます。一時停止、作成者が指定した表示状態、レイアウト変更には、引き続きプラグインの通常の表示状態・無効化経路を使う必要があります。
この明示的な設定を選んでも、updatePlacementのない登録は変わりません。AndroidはcaptureMode = CaptureMode.VIEWでない限りupdatePlacementを拒否します。ライブ動画などキャプチャ対象外のコンテンツは、failedFallback = NATIVEの下でネイティブ配置の追従を続けます。