ネイティブのハンドオフ
既存のレンダラーは、順序付けられたWebProjectionRequestメッセージをネイティブのスナップショットマネージャーのhandleへ転送してください。要求の検証、CSSからネイティブへの座標変換、キャッシュ画像のハンドオフ、複製の破棄をEngine内に集約できます。ブリッジはプラグイン自身のCapacitorまたはCordovaメソッドを使い続けます。Engineはどちらのフレームワークの呼び出しオブジェクトにも依存しません。
たとえば、レンダラーがツールバーボタンをまとめてネイティブへ更新し、各ボタングループに属するビューを把握しているとします。グループごとに座標変換と複製の破棄を再実装する代わりに、画像ハンドオフのメッセージをhandleへ転送します。グループのキャプチャ適格性と通常のレイアウトは、そのレンダラーに残します。Engineは、どの兄弟ビューを安全にまとめて隠せるかを推定できません。
ビューとスナップショットのライフサイクルAPIはメイン/UIスレッドで呼び出します。レンダラーから2つのコールバックを提供します。frames(ids, revision)は現在のキャプチャ可能な境界を、views(ids)は安全にまとめて非表示にできるネイティブビューの全体を返します。処理を利用できない場合は、空の境界またはnil / nullを返します。境界の単位は、iOSではWebViewに対する相対ポイント、AndroidではWebViewに対する相対物理ピクセルです。Engineは計測したIDが要求と完全に一致することを確認します。
snapshots.handle(request, in: webView,
frames: { ids, revision in renderer.measure(ids, at: revision) },
views: { ids in renderer.viewsAllowedToHide(ids) },
complete: reply)
snapshots.handle(request, activity.window, webView,
frames = { ids, revision -> renderer.measure(ids, revision) },
views = { ids -> renderer.viewsAllowedToHide(ids) },
complete = { result ->
result.fold(onSuccess = reply, onFailure = reject)
})
上のrendererとreplyは、プラグインの既存のレイアウト処理とブリッジ完了コールバックです。Androidでは、失敗したResultをブリッジの拒否コールバック(上のreject)へ渡します。Web側はその失敗を使い、画像を諦めて表示を再調整します。描画境界の失敗を成功として確認してはいけません。ジオメトリが安定する間、framesが繰り返し呼ばれることがあります。要求されたリビジョンとキャプチャ適格性を再確認し、レイアウトは変更しないでください。複数のコントロールが1つのネイティブコンテナを共有する場合、viewsは、未キャプチャのコントロールまで隠してしまう部分集合を拒否する必要があります。Engineはその関係を推定できません。ネイティブ操作の通知も、レンダラーの既存の仕組みを維持します。
coverは、それらのビューを隠す前に準備済みのピクセルを配置します。投影先のpartsはキャッシュ済みトークンと照合され、被覆と復元の両方に使われるため、準備済みサーフェスが移動しても古い位置は再利用されません。無効または利用できないハンドオフはshown: falseを返します。paintedとrestoredは対応する複製だけを破棄し、AndroidではWeb描画の準備完了を待ちます。releaseとresetは所有するリソースを破棄します。被覆や復元の経路でキャプチャを開始することはありません。
レンダラーは引き続き、restoreと受理された更新の確認応答の間に通常のネイティブ更新を行います。その更新の前に、iOSではrestoreControls()、AndroidではrestoreViews()を呼び、Engineが行った非表示変更だけを元に戻します。これは、任意のプラグインコンテンツを配置・再表示する指示ではありません。通常の可視性と配置の方針はレンダラーに残します。
スナップショットの直接操作
ハンドオフ中のネイティブ配置を連携側が所有する場合にのみ使います。NativeUIProjectionも、登録固有の配置方針と不透明ビューの方針で同じ操作を使います。要求をhandleへ転送する際、変換や複製の破棄を重複実装しないでください。
- CSSピクセルの投影先
partsを、iOSではwebView.bounds.width / viewportWidth、AndroidではwebView.width / viewportWidthで変換します。フレームをcoverとrestoreに渡します。coverのpartsがなければ、キャプチャ時の配置を維持します。どちらの操作も新しいピクセルをキャプチャしません。 transient: trueは、動く前面サーフェスを背景のcoveringTokenから分離します。ソースのサイズとスコープに対する相対位置が変わらない間、ピクセルを再利用します。固定スコープ内での移動はレイアウトを無効化します。- Web描画の準備完了後に移行時の複製を破棄します。iOSでは
retireCopy(token)、AndroidではretireCopyAfterWebDrawです。 - 戻す場合は
restoreを呼び、restoreControls()/restoreViews()でEngineの非表示変更を戻し、通常のネイティブレイアウトを適用します。ネイティブ更新が受理され、Web描画の準備が完了した後、retireCopyにentry: false/entry = falseを指定して復帰時の複製を破棄します。Androidではpaintedとrestoredをhandleへ転送すると、準備完了と拒否の処理を再利用できます。 release(token)でトークンを解放します。reset()は所有者の終了処理だけに使い、Androidのclose()でエンコーダーを破棄します。別のサーフェスのキャプチャをリセットしてはいけません。
キャプチャや配置がない場合は、レンダラーの通常のフォールバックを使います。ペイロードはWeb要求の契約を参照してください。
NativeUICoveredSnapshots.observeで操作の開始・終了をJSリスナーへ通知します。iOSではdrawHierarchy(in:afterScreenUpdates:)で可視ウィンドウをラスタライズし、クリップされたネイティブコンポーネントの背後も含めてキャプチャします。画面スナップショットビューは表示用の複製であり、ビットマップのソースには使いません。Liquid Glassのようなマテリアルでは、UIKitのラスタライズ結果が実際のコンポジターと異なる場合があります。プラグインが対応する背景で、実機の外観を検証してください。そのため、キャッシュの有効性にはレイアウトとプラグインの外観変更通知も含まれます。Engineは継続的にキャプチャせず、あらゆるレンダラーがキャプチャできるとも保証しません。
ネイティブのスクロール追従
Webのスクロールリスト内に置かれたネイティブコントロールは、そのままではリストの移動に合わせて新しい配置メッセージが必要です。レンダラーがすでにスクロールの調整を管理している場合にのみ、これらのバインディングを使います。一意に一致するiOSのoverflowスクロールビューに、ネイティブコンテンツを追従させられます。入れ子や曖昧なスクロールでは、誤ったスクロール所有者にコンテンツを取り付けるのではなく、通常のフォールバックを使ってください。
readScrollPlacement(element, owner, identify)は、変形されていない1つのoverflow所有者を計測します。createScrollBindings()は再利用を検証し、取得処理がACK待ちの間の移動を検出します。reuseはレイアウトの重複排除シグネチャに含めないでください。これはレイアウトではなく、確認応答の状態を表します。
iOSのNativeUIScrollBindings.replaceはソースを所有者ごとにまとめ、曖昧さなく一致するUIScrollViewだけにバインドします。update()はコンテンツを再構築せず、位置とクリッピングを適用します。NativeUIClippedViewはコンテンツ全体の寸法を維持しながら、表示ピクセルとヒットテストを制限します。
確立済みのバインディングがネイティブに拒否されると所有権を失い、同じ更新/ACK経路で新しく取得できます。新規取得が失敗した場合は、関連するレイアウトまたはスクロール終了イベントまでフォールバックに留まります。即時の再試行ループを追加したり、ユーザー操作を再実行したりしないでください。frameやpropsの変更で、確立済みの座標原点は再調整されません。
これは非公開のクラス名を使わず、WKWebView内部のoverflow階層を利用します。ビューの欠落・曖昧さ、入れ子のスクロール、ドキュメントのスクロール、未対応の変形、形状付きクリッピング、移動中で未確認の所有者にはバインドできません。UIScrollViewが存在するだけで対応しているとは判断しないでください。ネイティブビューはその内部で始まったジェスチャーを横取りする場合があります。その振り分けとSDKのアクセシビリティ動作は、引き続きレンダラー固有です。
iOSでレンダラーだけをキャプチャする
prepare要求にcaptureMode: 'view'を指定すると、ビューセレクターが返すレンダラービューだけを描画し、そのマテリアルの外側にある透明ピクセルを維持できます。デフォルトは引き続き、合成済みウィンドウのキャプチャです。レンダラーだけのキャプチャでは、背景に依存するぼかしや屈折が変化したり、省略されたりする場合があります。ガラスサーフェス内の透明性を保証するものではありません。レンダラービューが存在しない、非表示、または切り離されている場合、準備は拒否されます。Androidや古いネイティブバイナリは既存のキャプチャ経路を維持します。
移動するDOMサーフェスは、WebProjectionOptions.imageSurface(HTMLElement)を通じて、ビューポートに固定された転送用レイヤーを提供できます。Web投影は準備要求にimageSurface: 'web'を付け、ネイティブの非表示処理がレンダラーのレイアウトを維持するようにします。変わるのは配置であり、ハンドオフのプロトコルではありません。すべてのサーフェスが、同じ一時的なネイティブの移行時・復帰時コピーを使います。移動中は、デコード済みDOM画像をソースサーフェスに置き、その変形に追従させます。平行移動では再利用可能なキャプチャを無効化しません。寸法は表示ピクセルの解像度で比較しますが、実際のサイズ変更やソースの変更は引き続き無効化します。iOSでは、各画像パーツに、画像の切り抜き領域を基準としたビューポート単位の任意のanchorBoundsが含まれます。レンダラーは、グループ内の最初の実際のツールバー項目など、複合コントロール向けにanchorFramesを提供できます。移動するターゲットはこれらの境界を使い、キャプチャ時のネイティブ座標とその後のDOM配置を混同せず、キャプチャの余白を保持します。
DOM画像がピクセルを所有する間も、ネイティブレンダラーとソースの所有権を維持してください。移行時には、共通のハンドオフが同じネイティブ処理内で準備済みのネイティブピクセルを配置してライブレンダラーを隠し、その後、ソースサーフェス上にDOM画像を表示します。この転送中、DOM画像は一時的なネイティブコピーとビューポート座標を共有します。Webの描画後、Engineはネイティブコピーを取り除き、DOM画像をソースサーフェスへ戻します。ライブの半透明コントロールが移行中に自身のWeb画像をサンプリングすることはありません。任意のlayoutFrame(target, part, parts)アダプターは、キャプチャしたグループから現在のネイティブアンカーの配置を提供します。可視性やハンドオフのタイミングは管理しません。復帰時にはその配置を使い、ネイティブ更新が受理されるまで、準備済みの両コピーを同じ移動先で固定します。レンダラーは通常のネイティブレイアウトを適用し、受理の確認応答を受けてupdated(token, ids, revision)を呼びます。DOM画像を取り除けるのは、復帰要求より新しいリビジョンだけです。キャッシュされたキャプチャが再利用可能かどうかにかかわらず、表示中の画像のリースは、再表示の確認応答または明示的な終了まで維持されます。どちらの転送中にもスナップショットはキャプチャしません。
iOSでレイアウトを計測可能に保つ必要がある場合は、メインスレッドでNativeUICoveredSnapshots.concealPixels(in:)を使います。これはisHiddenやジオメトリを変更せず、ピクセルをマスクし、操作とアクセシビリティを無効にします。返される復元クロージャーは冪等です。リースが重複している場合は、最後のリースが終わって初めて、元のマスクとフラグを復元します。合成済みキャプチャとレンダラーだけのキャプチャは、同じネイティブコピーと描画完了の契約を使います。