iOS 27 における SwiftUI のパフォーマンスと相互運用
LazyVStack は自分自身の高さを知りません。すでに配置したビューの平均サイズと、これから残ると予想される数から自分の高さを推定し、その推定値をスクロールに合わせてリアルタイムに補正していきます。1 WWDC26 の UI Frameworks チームによる3本のセッションは、このたった一つの事実(とグラフィックスや相互運用におけるその類例)を出発点にして、iOS 27 で負荷がかかったときに SwiftUI がどう振る舞うのかという実用的なモデルへと組み上げていきます。スクロールがどうやって滑らかさを保つのか、GPU 効果がどう合成されるのか、そしてすでに出荷済みの AppKit や UIKit のアプリに SwiftUI がどう組み込まれるのか、という問いです。
3本のセッションは、一つの主張を3通りの形で語ったものとして読めます。lazy stack はその推定と戦うのをやめたときに高いパフォーマンスを発揮し、シェーダー効果は各 modifier をパイプラインの一段階として扱ったときに合成でき、相互運用は @Observable と representable プロトコルに継ぎ目を担わせたときにうまくいきます。3本のどれもが機能の発表ではありません。それぞれが、フレームワークを推測するのではなく予測できる程度まで丁寧に説明されたメカニズムなのです。
TL;DR / 要点
LazyVStackが評価するのは可視領域を埋めるビューだけです。画面外の高さやコンテンツオフセットは推定値なので、絶対的なコンテンツオフセットの読み取りは不安定です。.onScrollTargetVisibilityChangeのような相対的な可視性の API を優先しましょう。12- プリフェッチは、ビューを表示する作業をそのビューが現れる前の複数フレームにまたがって分割します。プリフェッチした作業が無駄にならないよう、ビューのセットアップは(
onAppearではなく)イニシャライザで行いましょう。1 ForEachの葉でサブビューの数を動的にするのは避けましょう。body 内で条件分岐によってフィルタリングするとビューがインデックスごとに生き残ってしまうため、データレベル(QueryのPredicate)でフィルタリングしてください。1- SwiftUI は3つのシェーダーのエントリポイント(
colorEffect、distortionEffect、layerEffect)を、能力の高い順に提供します。隣接ピクセルをサンプリングできるのはlayerEffectだけで、これはブラーやドメインワープに必要なものです。3456 - シェーダーはステートレスなので、アニメーションは
TimelineViewのタイムスタンプをパラメータとして渡すことで生まれます。フレーム間で何も持ち越されません。37 - AppKit と UIKit は
@Observableから自動的な再描画を得られ(手動のneedsDisplayはもう不要です)、SwiftUI はNSHostingView、NSGestureRecognizerRepresentable、NSHostingMenu、NSHostingSceneRepresentationを通じて既存のアプリに組み込めます。8910
Lazy Stack は推定で動く
LazyVStack が上から下へとレイアウトを進め、可視領域が埋まった時点で止まり、残りは推定すると説明しています。
lazy stack についてまず腹落ちさせるべきは、それが意図的に正確さと引き換えに効率を取っているという点です。VStack とは違い、LazyVStack は可視でないビューを評価もレンダリングもしません。ビューを上から下へとレイアウトし、可視領域が埋まった時点で止め、スクロールで入ってくるビューを追加し、出ていくビューを取り除きます。1 その見返りは明白です。一方でコストは微妙です。stack はすべてのビューを読み込むことがないため、画面外のビューの高さはそれ以前のビューの平均から推定され、理想的な幅は最初のサブビューの幅へと縮められ、可視領域より上の空間そのものも近似値になります。1
この推定は一度回避すれば済むバグではありません。それ以外のすべての lazy stack の判断が乗る土台です。セッション 321 は、画面回転を例にこの帰結を具体的に示します。iPhone を回転させると、最上部の可視ビューはアンカーされたまま留まりますが、stack はそれより上のビューの正確な新しいレイアウトをまだ計測していません。一番上までスクロールして戻ると、stack は辻褄を合わせなければなりません。可視領域より上の推定された空間を補正し、scroll view のコンテンツオフセットを同じ量だけ更新して、一番上でのコンテンツオフセットがゼロに着地するようにします。1 lazy stack と、それを囲む scroll view は、位置とオフセットを精密に協調させているため、推定値が更新されても可視サブビューの相対位置が飛ぶことはありません。1
そこから導かれる実践的な帰結は、どのスクロール API を信頼すべきかという規則です。絶対的なコンテンツオフセットは推定値なので、それを読み取って(たとえば .onScrollGeometryChange で、100ポイント過ぎたらボタンを隠すなど)しきい値を決めると、推定値が落ち着くにつれてそのしきい値はずれていきます。2 安定したシグナルは相対的な可視性です。.onScrollTargetVisibilityChange modifier は、scroll view 内で可視なサブビューの集合が変化したときに発火するので、「ショーケースへスクロール」ボタンは、不安定なピクセル数ではなく、どの行が画面に表示されているかにしきい値(セッションでは80%を使用)を紐づけて可視性を制御できます。21 同じ論理は .scrollTransition にも当てはまります。ビューを本来のフレームの外へ押し出すような変形は、stack に「可視なビューが画面外にある」と思い込ませ、早すぎる段階でドロップさせかねません。そのため、どのようなスクロールトランジションでも、本来は可視でないはずのビューを可視領域の中へ押し込まないようにしなければなりません。111
なぜビューの struct がそのままサブビューではないのか
lazy stack が読み込むサブビューは、あなたが書いたビューの struct と一対一には対応しません。StepView の ForEach は1ステップにつき1つの StepView に解決されますが、各 StepView の body が、囲むレイアウトなしに2つのトップレベルビュー(図と説明)を返す場合、stack はそれらをそれぞれ別々に読み込みます。1 stack にとって意味を持つ数は、struct の数ではなく、解決されたサブビューの数です。
罠は動的なサブビュー数です。StepView が環境値に応じてサブビューを1つ返したり0個返したりすると、それ以前のビューの数が変わりうるため、stack はもうインデックスを信頼できなくなります。そこで stack は念のため、それ以前の StepView インスタンスを生かし続けます。これは、無関係な環境の変化が、画面外にスクロールされたビューの body 評価を引き起こしかねず、stack はそれらの状態を解放しないということを意味します。1 修正方法は、フィルタをビューから外してデータ側へ移すことです。SwiftData を使っているなら、条件を Query の Predicate に入れれば、ビューを一切構築せずにサブビュー数が判明します。1 body 内でオプショナルをアンラップするのも同じく寿命を延ばす効果があります。より整然とした手は、lazy stack に部分的にしか解決されていない行を保持させるのではなく、もっと上の階層で ContentUnavailableView を表示することです。1
プリフェッチは、その推定を速く感じさせるためのメカニズムです。スクロール中、scroll view はフレームの締め切りまでにオフセットの更新、ビューのレンダリング、そしてあなたのオフセット変更処理を済ませなければなりません。新しいビューの表示がその予算を超過すると、フレームが落ちてカクつきが見えます。1 それを防ぐため、lazy stack は余裕時間があるかを確認し、もしあれば、まもなく現れるビューの作業の一部を前倒しで行います(body とレイアウトを評価し、入れ子になった LazyHStack を複数フレームに分散させることさえします)。こうしてビューが現れる頃には、ほとんどの作業がすでに終わっているのです。1 これこそ、セッションが onAppear について強く念を押す理由です。onAppear でビューをセットアップすると、プリフェッチした作業を捨てることになり、ビューが現れたときにやり直しを強いられ、ときには必要以上のビューを巻き込んでスクロールを劣化させます。ビューがまともな状態で到着するよう、セットアップはイニシャライザで行い、onAppear は無限スクロールで次のページを取得するような、純粋に出現に紐づいた作業のためにとっておきましょう。1 逆順のスクロールでは、プリフェッチの最中に body が実行される一方、onAppear がまったく発火しないことすらあります。1
高度なグラフィックスは単なるパイプライン
セッション 322 は「高度なグラフィックス」を合成として捉え直します。SwiftUI の各 modifier は、データを受け取り、変換し、次へ渡すパイプです。高度な結果は、単一の複雑な API にではなく、パイプをどうつなぐかに宿ります。3 このセッションは、ありふれた段階を連ねて Apple Music 風のライブ歌詞ビューを組み立てます。カバーアートをブラーして後退させ、その上にシェーダーを走らせ、そのシェーダーを時間で駆動し、歌詞テキストのスクロールを同じ時間源に同期させる、という具合です。3
シェーダーの段階こそが、本当の選択どころです。SwiftUI は Metal のシェーダー関数を、能力が段階的に高まる3つのエフェクトのエントリポイントを通じて呼び出します。colorEffect は、各ピクセルの位置と元の色を与えられて、その色を変換します。グレースケール変換のようなものにはこれで十分です。4 distortionEffect は代わりに、ある位置を別の位置へマッピングします(「この位置の色は、あの位置からサンプリングせよ」と SwiftUI に伝えます)。色を一切伴わない幾何学的なゆがみを扱います。5 layerEffect は最も柔軟です。シェーダーにビュー全体のレイヤーを渡すので、出力ピクセルは隣接ピクセルや領域全体をサンプリングできます。これはまさに、ブラーやより豊かなゆがみが必要とするものです。63
セッションのドメインワープ背景は layerEffect を使います。一様な float2 のオフセットは、すべてのピクセルを同じ量だけずらすので、画像をスライドさせるだけです。有機的な動きにはピクセルごとの変化が必要なため、シェーダーは事前計算された NoiseTexture(画像として渡され、Metal 側では texture2d として届きます)をサンプリングします。その赤と緑のチャンネルが、各 UV 座標で異なる X と Y のオフセットを供給します。3 ノイズを一度サンプリングすると画像がねじれ、二度目を一度目のサンプルがずらした位置でサンプリングすると、流れるような塊が生まれます。この二次のテクニックがドメインワープであり、セッションはパラメータをライブプレビューできるダウンロード可能なサンプルアプリを紹介しています。3
このアニメーションを成立させるフレームワーク上の事実が2つあります。シェーダーはステートレスです。前のフレームの記憶を一切持たず、出力はあなたが渡すパラメータだけに依存します。3 したがって動きはシェーダーの内側からは生まれません。外から供給しなければならず、それを供給するパイプが TimelineView です。アニメーションのスケジュールに乗せると、毎フレーム、タイムスタンプとともに発火します。73 そのタイムスタンプをシェーダーに渡し、ノイズのサンプル位置に加えると、パターンが流れます。歌詞テキスト側は、同じ時間源を逆方向から再利用します。再生のタイムスタンプが現在の行を選び(太く鮮明に、他は薄く)、onChange が時間の進行に合わせてその行を中央に保ちます。123 アクティブな行の上に浮かぶタイムスタンプは、offset(両方のビューのサイズを必要とします)ではなく、アライメントの基準点を意味的に再定義する alignment-guide のオーバーライドで配置されます。これにより、手動のオフセットなしに、サブビューの上端がコンテナの下端に取り付きます。133
SwiftUI は AppKit や UIKit のアプリに組み込める
相互運用のセッションは、導入という問い全体を捉え直す指摘から始まります。ほとんどのアプリは、すでに暗黙のうちに SwiftUI を使っているのです。新しいデザインでは、NSSlider、NSSwitch、NSSegmentedControl といった AppKit のコントロールが内部で SwiftUI でレンダリングされており、Liquid Glass もまた SwiftUI を通じてフレームワーク間で実装の大部分を共有しています。8 つまり「SwiftUI を導入する」とは、書き直しというより、どこで継ぎ目を明示するかという判断なのです。
最初のステップに SwiftUI はまったく必要ありません。AppKit と UIKit は、@Observable 型を自動的に監視するようになりました。モデルクラスに @Observable を付け、drawKnob のような描画メソッド内でそのプロパティを読むと、AppKit は各アクセスを追跡し、アクセスされたプロパティのいずれかが変化したときに再描画します。あるスライダーの値が別のスライダーの見た目に影響するたびに書いていた手動の needsDisplay = true は不要になります。814 この監視は draw(_:) を超えて updateConstraints()、layout()、updateLayer()、そして NSViewController の相当メソッドにまで及び、UIKit ではさらに UIButton、UICollectionViewCell などにまで届きます。8 2026年のリリースではデフォルトで有効になっており、Info.plist 経由で macOS 15(NSObservationTrackingEnabled)と iOS 18(UIObservationTrackingEnabled)にバックデプロイ可能です。8
モデルが @Observable になれば、実際の SwiftUI の継ぎ目は小さくなります。セッションは、スライダーベースのカラーピッカーを、Canvas(drawRect に類する即時モードの API で、既存の Core Graphics コードを再利用する withCGContext を備えます)で描く円形の SwiftUI コントロールとして作り直します。まさに同じ @Observable ColorModel を再利用しています。158 AppKit がビューを期待する場所に埋め込むには、NSView のサブクラスである NSHostingView でラップします。モデルがすでに更新を駆動しているので、必要なのはそのラップだけです。168 既存のジェスチャーコードも書き直さずに引き継げます。ForceClickGestureRecognizer は NSGestureRecognizerRepresentable(makeNSGestureRecognizer と handleNSGestureRecognizerAction を実装します)を通じて SwiftUI のビューに到達し、通常の .gesture modifier で取り付けられて、SwiftUI 自身のドラッグジェスチャーと共存します。178 同じ representable のファミリーには、逆方向に NSView を埋め込むための NSViewRepresentable も含まれます。8
この継ぎ目はメニューやシーンへとスケールアップします。Button と Picker を持つ SwiftUI の View は、NSHostingMenu(NSMenu のサブクラス)を通じて本物のメニューになり、メインメニューに追加された NSMenuItem のサブメニューとして設定されます。keyboardShortcut は、force-click できない入力デバイスのために、そのアクションへジェスチャー以外の経路を与えます。188 SwiftUI のシーン全体も取り付けられます。MenuBarExtra は NSHostingSceneRepresentation を通じて既存のアプリに到達し、applicationWillFinishLaunching 内の addSceneRepresentation 経由で追加されます。Settings シーンの Toggle がエクストラを挿入するかどうかを制御し、openSettings() の環境アクションが @IBAction から設定を開きます。198 セッションの締めくくりの指摘こそが要となるものです。取り上げられたすべての API は2026年のリリースかそれ以前で出荷されており、恩恵を受けるためにアプリが完全に SwiftUI である必要はまったくありません。8 とはいえ、このサイクルの別の場所では近代化の圧力はもっと厳しい一面を持ちます。iOS 27 はUIKit のシーンベースのライフサイクルを起動の必須要件にしているので、最新の SDK で再ビルドしたアプリがシーンを一度も採用していなければ、起動に失敗します。
SwiftUI チームがラボで補足したこと
WWDC26 UI Frameworks のグループラボから得られた2つの補足が、各セッションが説明する無効化(invalidation)モデルをより鋭くします。どちらもローカルで文字起こしした録音からの言い換えです。Apple はラボの公式キャプションを公開しておらず、これはSwift チーム自身のグループラボ(言語エンジニアが並行処理とロードマップの質問に答えた場)でも一貫していたのと同じキャプションの欠落です。
コードを body から計算プロパティへ移しても、無効化の面では何の利点もありません。SwiftUI は body を再実行するたびにそのプロパティも再実行するので、この移動は可読性の勝利にすぎません。20 パフォーマンスの境界は一段上に現れます。別のビュー型に切り出せば、SwiftUI はそれを独立に無効化でき、入力が変化したときに囲む body 全体ではなくそのビューだけを再実行します。SwiftUI の作業を減らしたいときは、新しいプロパティではなく新しいビューに手を伸ばしましょう。
あらゆる環境の変化は、その環境値を読むすべてのビューを無効化します。環境の読み取りは安価ですが、環境のめまぐるしい変化は安価ではありません。21 速く変化する値は環境から外しておきましょう(パネルの例は現在時刻でした)。毎フレーム更新される値は、動くたびにすべての読み手を再評価に引きずり込むからです。変わりやすい値はそれを必要とする経路に沿って下ろし、環境にはじっとしているものを運ばせましょう。
後のラボセッションは、さらに3つのメカニズムの詳細を加えます。いずれも WWDC 2026 SwiftUI グループラボのローカルで文字起こしした録音からの言い換えで、Apple はこれの公式キャプションを公開していません。
部分的なグラフ評価が、プリフェッチした作業が実際にどこへ行くのかを説明します。パネルは、lazy stack が、現在のフレームをレンダリングした後に残ったフレーム時間で、これから来るセルの body を評価し、次のフレームが始まる直前で止まると述べました。22 彼らが指摘した落とし穴は、セルのサイズを変えてしまうことで再レイアウトを強いる onAppear が、そのプリフェッチした作業を捨ててしまう点です。彼らの助言は、サイズに関する作業を body や onAppear ではなくセルのイニシャライザで行い、プリフェッチが生き残るようにすることでした。22
相互運用のメンタルモデルは、UIKit に10年以上携わってきたパネリストから出ました。UIKit はウィンドウから内側の葉へと、上から下へレイアウトします。一方 SwiftUI は最も内側のノードから外側へと、下から上へ組み立てます。22 2つが交互に組み合わさるとき、パネルはこの層が交互になる配置をサンドイッチやケーキと呼びました。深く交互に組み合わさると微妙になるのはこのためです。各フレームワークが、レイアウトのパスを反対の端から駆動しようとするのです。22
上述の環境に関する指摘には、生々しい実世界の例が付きました。今まで見た最悪の環境の誤用を尋ねられたパネルは、スクロール位置を環境に入れることを挙げました。これはスクロール中に毎フレーム更新され、その結果、毎フレームすべての読み手を無効化してしまうのです。22
2回目の SwiftUI グループラボのセッションは、さらに4つのメカニズムの詳細を加えました。いずれも WWDC 2026 SwiftUI グループラボ(セッション2)のローカルで文字起こしした録音からの言い換えで、Apple はラボの公式キャプションを公開していません。
onGeometryChange(for:of:action:) modifier は、その2つのクロージャがどう作業を分担するかのおかげで、聞こえるよりも読みやすく書けます。transform クロージャはライブのジオメトリで毎フレーム実行されますが、それが返す値だけがアクションの発火を制御します。結果の型は Equatable であり、アクションはその値が変化したときにだけ実行されるからです。23 そのため、粗い値(生のサイズではなく、サイズのバケットやレイアウトのブレークポイント)を返せば、フレームレートのシグナルが、連続的にではなく、しきい値で2回だけ発火するシグナルに変わります。パネルはこれに、GeometryReader はそれが包むサブビューにとって高コストなので、メインのレイアウトを駆動せずに計測できるよう background に閉じ込めるべきだ、という警告を添えました。23
優れた dynamic property は、ほとんどの onChange の作業をそっくり置き換えられます。DynamicProperty の update() メソッドはビューの body の直前に実行されるので、カスタムの property wrapper はその時点で、すでにキャッシュされた値(パネルの例は画像でした)を同期的に差し出し、onAppear の往復とそれが引き起こす再レンダリングを省けます。24 パネルの言い回しは、onChange のほとんどの用途は、よく作られた dynamic property で置き換えられる、というものでした。24
ScrollView の中で、報告したレイアウト境界の外に描画するビューは、カリングされることがあります。システムが、実際に描画している場所ではなく、伝えられた境界からそのビューを画面外だと判断するからです。25 パネルはこれを、ホストビューからはみ出して描かれ、その後スクロールの途中で消えてしまうカスタムのドロップダウンやオーバーレイの背後にある具体的な失敗として名指しし、アンカーを越えて広がる overlay のコンテンツにも同じ危険が当てはまると述べました。25
シートを含むすべての上にフルスクリーンのオーバーレイを表示することには、きれいな純粋 SwiftUI の解はありません。パネルの助言は、UIKit のシーンライフサイクルを通じて新しい UIWindow に降りることで、より深い原則は「最後のウィンドウが勝つ」ことと、何が一番上に乗るのかについて単一の信頼できる情報源がなければならないことです。26 彼らは、より良い修正は多くの場合、すべての上に妨げになるカバーを投げかけることではなく、ユーザーが期待するナビゲーションスタックを復元することだ、と付け加えました。26
まず何を採用すべきか
メカニズムとして説明されたリリースは、目新しさではなくレバレッジによる順序付けに報います。
- AppKit/UIKit のコードで、モデルクラスを
@Observableに切り替える。 手動のneedsDisplay呼び出しを削除し、監視するすべての描画・レイアウトメソッドに自動的な再描画を与え、後のNSHostingViewの組み込みを些細なものにする前提条件になります。814 これは3本のセッション全体で最もリスクが低く、最も即効性のある一手です。 - lazy stack に動的なサブビュー数がないか監査する。 条件によって0個または1個のサブビューを返したり、body でオプショナルをアンラップしたりする
ForEachの葉は、ビューをインデックスごとに生かし続けています。フィルタをQueryのPredicateに(あるいは階層のより上に)移せば、メモリとアイテムへのスクロール性能の両方が改善します。1 - ビューのセットアップを
onAppearから外してイニシャライザに入れる。 プリフェッチは、プリフェッチした作業が生き残ったときにだけ役立ちます。onAppearでサイズやコンテンツを変えるセットアップは、その作業を捨ててしまいます。1 これは静かで幅広く効くスクロールの滑らかさの勝利です。 - 絶対的なスクロールオフセットの読み取りを、相対的な可視性の API に置き換える。 コンテンツオフセットに紐づいたものはすべてずれていきます。
.onScrollTargetVisibilityChangeは、実際にどの行が可視かに紐づきます。21 - シェーダーには、小さな効果がその場所に値するときだけ手を伸ばす。
colorEffectかdistortionEffectから始め、効果が隣接ピクセルをサンプリングしなければならないときだけlayerEffectへ引き上げ、動きはシェーダー内に状態を期待するのではなくTimelineViewのタイムスタンプで駆動しましょう。4567
3本を貫く一本の筋は、押し込む前にフレームワークを予測せよ、ということです。lazy stack は推定し、シェーダーは忘れ、相互運用は書き直しではなく継ぎ目です。この3つの事実を念頭に組み立てれば、残りはついてきます。
FAQ
SwiftUI の lazy stack でスクロール位置が飛んだりずれたりするのはなぜですか?
LazyVStack は画面外のビューを読み込まないため、その高さと可視領域より上の空間を推定します。その結果、絶対的なコンテンツオフセットは推定値となり、フレームワークが実際のレイアウトを学ぶにつれて補正します(たとえば画面回転の後、一番上までスクロールして戻ると推定値の辻褄が合わされます)。1 UI を絶対的なオフセットに紐づけると、推定値が落ち着くにつれてしきい値がずれます。代わりに、実際にどのサブビューが可視かに基づいて発火する .onScrollTargetVisibilityChange を使いましょう。2
iOS 27 の SwiftUI の lazy stack でスクロールを滑らかに保つには?
プリフェッチに仕事をさせましょう。lazy stack がビューの出現前に行う作業が捨てられないよう、ビューはイニシャライザでセットアップし、onAppear でビューのサイズやコンテンツを変えるのは避けます。1 また、ForEach の葉でサブビューの数を動的にするのを避け、ビューの出現後のレイアウト変更(onGeometryChange で駆動される高さなど)を避けましょう。どちらも stack に作業のやり直しやスクロール途中での位置の再計算を強いるからです。1
colorEffect、distortionEffect、layerEffect はいつ使えばよいですか?
colorEffect は、各ピクセルの位置と元の色からその色を変換するのに使います(たとえばグレースケールフィルタ)。4 distortionEffect は、出力位置をサンプリング元のソース位置にマッピングする幾何学的な効果に使います。5 layerEffect は、出力ピクセルが2つ以上の入力ピクセルに依存するときに使います。シェーダーにビューのレイヤー全体を与えて、隣接ピクセルや領域全体をサンプリングさせるからで、これはブラーやドメインワープが必要とするものです。6
SwiftUI で Metal シェーダーをアニメーションさせるには?
シェーダーはステートレスです。前のフレームの記憶を持たず、パラメータだけに依存するので、シェーダーの内側からはアニメーションできません。3 時間とともに変化する値を供給しましょう。アニメーションのスケジュールに乗せた TimelineView は毎フレーム、タイムスタンプとともに発火します。そのタイムスタンプをパラメータとしてシェーダーに渡せば(セッションではノイズのサンプル位置に加えています)、効果がアニメーションします。73
既存の AppKit や UIKit のアプリに、書き直さずに SwiftUI を追加できますか?
できます。恩恵を受けるためにアプリが完全に SwiftUI である必要はまったくないと、セッションは明言しています。8 AppKit と UIKit が自動的に再描画するようモデルに @Observable を付け、それから NSHostingView で SwiftUI のビューを埋め込み、NSGestureRecognizerRepresentable で既存のジェスチャーレコグナイザーを引き継ぎ、NSHostingMenu でメニューを作り、NSHostingSceneRepresentation で app delegate から SwiftUI のシーンを取り付けます。これらはすべて2026年のリリースかそれ以前で出荷されています。816171819
Apple Ecosystem クラスター全体:SwiftUI の基盤(result builder、不透明型、値型のビューツリー)は、lazy stack がビューの struct をなぜ異なるサブビューの集合へ解決するのかを説明します。この性能と相互運用の話のかたわらに位置するiOS 27 SwiftUI の新機能(並べ替え、ドキュメント、ツールバー、エラー)。いまや AppKit と UIKit でも自動的な再描画を駆動する@Observable の内部。そして相互運用のセッションがそのフレームワーク横断の実装を共有 SwiftUI に帰するLiquid Glass のパターン。ハブはApple Ecosystem シリーズです。AI エージェントを伴う iOS のより広い文脈については、iOS Agent Development ガイドをご覧ください。
References
-
Apple, WWDC26 session 321, “Dive into lazy stacks and scrolling with SwiftUI.” developer.apple.com/videos/play/wwdc2026/321. Covers lazy-stack layout and height estimation, the estimated content offset, view-struct-to-subview resolution, the dynamic-subview-count trap, prefetching across frame deadlines, and
onAppearversus initializer setup. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
Apple, WWDC26 session 321, “Dive into lazy stacks and scrolling with SwiftUI”. The session presents
onScrollTargetVisibilityChange(a modifier whose closure runs when the set of visible scroll targets changes) as the stable, relative-visibility alternative to absolute content-offset reads. ↩↩↩↩↩ -
Apple, WWDC26 session 322, “Compose advanced graphics effects with SwiftUI.” developer.apple.com/videos/play/wwdc2026/322. Frames effects as a composable pipeline; covers blur, the three shader entry points, the
NoiseTexturedomain-warp technique, stateless shaders driven by time, and alignment-guide attachment. ↩↩↩↩↩↩↩↩↩↩↩↩↩ -
Apple Developer Documentation:
colorEffect(_:isEnabled:). Returns a new view that applies a shader transforming each pixel’s color, given its position and original color. ↩↩↩↩ -
Apple Developer Documentation:
distortionEffect(_:maxSampleOffset:isEnabled:). Applies a shader that maps the position of each pixel to a source position to sample from, for geometric effects. ↩↩↩↩ -
Apple Developer Documentation:
layerEffect(_:maxSampleOffset:isEnabled:). Applies a shader as a layer effect with access to the entire view layer, allowing each output pixel to sample multiple input pixels. ↩↩↩↩ -
Apple Developer Documentation:
TimelineView. A view that updates its content according to a schedule; on an animation schedule it supplies the per-frame timestamp session 322 feeds into its shader. ↩↩↩↩ -
Apple, WWDC26 session 272, “Use SwiftUI with AppKit and UIKit.” developer.apple.com/videos/play/wwdc2026/272. Covers automatic
@Observableredraw in AppKit/UIKit, observation back-deployment via Info.plist,Canvas,NSHostingView,NSGestureRecognizerRepresentable,NSHostingMenu, andNSHostingSceneRepresentation. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
Apple Developer Documentation:
NSGestureRecognizerRepresentable. A protocol that wraps anNSGestureRecognizerfor use as a SwiftUI gesture, implemented withmakeNSGestureRecognizerandhandleNSGestureRecognizerAction. ↩ -
Apple Developer Documentation:
MenuBarExtra. A scene that renders a menu bar item; session 272 attaches it to an existing AppKit app throughNSHostingSceneRepresentation. ↩ -
Apple Developer Documentation:
scrollTransition(_:axis:transition:). Applies a transition as a view scrolls within a scroll view; session 321 warns that a transform pushing a view into the visible rect can desync a lazy stack. ↩ -
Apple Developer Documentation:
onChange(of:initial:_:). Runs an action when a value changes; session 322 uses it to recenter the current transcript line. ↩ -
Apple Developer Documentation:
alignmentGuide(_:computeValue:). Sets a view’s alignment guide so the layout system positions it semantically; session 322 overrides a bottom guide to attach a subview’s top edge to its container’s bottom edge. ↩ -
Apple Developer Documentation:
Observable. The macro that makes a class’s mutable properties participate in the Observation system, which AppKit and UIKit track for automatic redraw in the 2026 releases. ↩↩ -
Apple Developer Documentation:
Canvas. An immediate-mode drawing view whose closure receives aGraphicsContext; session 272 uses it to redraw the circular color picker and noteswithCGContextfor reusing Core Graphics code. ↩ -
Apple Developer Documentation:
NSHostingView. AnNSViewsubclass that hosts a SwiftUI view hierarchy inside an AppKit view tree. ↩↩ -
Apple Developer Documentation:
NSViewRepresentable. A wrapper that lets anNSViewparticipate in a SwiftUI view hierarchy; session 272 names it alongsideNSGestureRecognizerRepresentableas part of the representable family. ↩↩ -
Apple Developer Documentation:
NSHostingMenu. AnNSMenusubclass that renders a SwiftUI view as menu content, added to the main menu as the submenu of anNSMenuItem. ↩↩ -
Apple Developer Documentation:
keyboardShortcut(_:modifiers:). Assigns a keyboard shortcut to a control’s action; session 272 adds one to the menu button so input devices that cannot force-click still reach the feature. ↩↩ -
Apple, WWDC 2026 UI Frameworks group lab, session 8002. Paraphrased from a locally transcribed recording; no official transcript is published. The team clarified that moving code from
bodyinto a computed property is a readability change only, and that the independent-invalidation boundary appears when you extract a separate view type. ↩ -
Apple, WWDC 2026 UI Frameworks group lab, session 8003. Paraphrased from a locally transcribed recording; no official transcript is published. The team noted that every environment change invalidates all views reading that value, and advised keeping fast-changing values (the panel’s example was the current time) out of the environment. ↩
-
Apple, WWDC26 session 8006, “SwiftUI Group Lab.” developer.apple.com/videos/play/wwdc2026/8006. Paraphrased from a locally transcribed recording of the WWDC 2026 SwiftUI Group Lab; Apple publishes no official captions for the labs. Source for partial graph evaluation in leftover frame time (and the
onAppear-resize warning, with sizing done in init), the UIKit-top-down-versus-SwiftUI-bottom-up interop “sandwich or cake” arrangement, and the scroll-position-in-the-environment example that invalidates every reader on every frame. ↩↩↩↩↩ -
Apple, WWDC 2026 SwiftUI Group Lab (session 2). Paraphrased from a locally transcribed recording of the WWDC 2026 SwiftUI Group Lab (session 2); Apple publishes no official captions for the labs. Source for the
onGeometryChangetransform-gates-action mechanism (return a coarse value to fire at thresholds) and the guidance to confine an expensiveGeometryReaderto a background. The two-closure shape is documented atonGeometryChange(for:of:action:): theof:transform closure derives anEquatablevalue from the geometry proxy and theaction:closure runs only when that value changes. ↩↩ -
Apple, WWDC 2026 SwiftUI Group Lab (session 2). Paraphrased from a locally transcribed recording of the WWDC 2026 SwiftUI Group Lab (session 2); Apple publishes no official captions for the labs. Source for replacing most
onChangework with a dynamic property that vends a cached value synchronously. Apple’sDynamicPropertyprotocol defines anupdate()method that SwiftUI calls immediately before rendering a view’sbodyso the property holds its most recent value. ↩↩ -
Apple, WWDC 2026 SwiftUI Group Lab (session 2). Paraphrased from a locally transcribed recording of the WWDC 2026 SwiftUI Group Lab (session 2); Apple publishes no official captions for the labs. Source for the culling of views that draw outside their reported layout bounds inside a
ScrollView(the failure behind overflowing custom dropdowns and overlays), and the note that the same hazard applies tooverlaycontent extending past its anchor. ↩↩ -
Apple, WWDC 2026 SwiftUI Group Lab (session 2). Paraphrased from a locally transcribed recording of the WWDC 2026 SwiftUI Group Lab (session 2); Apple publishes no official captions for the labs. Source for the lack of a clean pure-SwiftUI full-screen-over-sheets overlay, the drop to a new
UIWindowthrough the UIKit scene life cycle, the “last window wins / single source of truth” principle, and the preference for restoring the navigation stack over a disruptive cover. ↩↩