← 所有文章

iOS 27 中的 SwiftUI 效能與互通

一個 LazyVStack 並不知道自己有多高。它會依據已放置檢視的平均尺寸,以及預期還剩下的數量,來估算自己的高度,然後在您捲動時即時修正這個估算值。1 來自 UI Frameworks 團隊的三場 WWDC26 議程,便是以這一項事實(以及它在圖形與互通領域的對應現象)為基礎,建構出一套可運作的模型,說明 SwiftUI 在 iOS 27 高負載下的行為:捲動如何維持流暢、GPU 效果如何組合,以及 SwiftUI 如何嵌入您早已上架的 AppKit 或 UIKit App。

這三場議程其實是同一套論點的三種講法。當您不再與 lazy stack 的估算機制對抗時,它便表現良好;當您把每個修飾符都視為管線中的一個階段時,著色器效果便能組合;而當您讓 @Observable 與 representable 協定承載接縫時,互通便能運作。三者都不是功能發表,而是把機制解釋得夠透徹,讓您能夠預測框架的行為,而不是用猜的。

TL;DR / 重點摘要

  • 一個 LazyVStack 只會評估填滿可見區域的檢視;螢幕外的高度與內容偏移量都是估算的,因此讀取絕對內容偏移量並不穩定,您應改用相對可見性的 API,例如 .onScrollTargetVisibilityChange12
  • 預先擷取(prefetching)會把顯示一個檢視的工作分散到它出現前的多個影格進行;請在初始化器(而非 onAppear)中設定檢視,這樣預先擷取的工作才不會被丟棄。1
  • 避免在 ForEach 葉節點中使用動態變動的子檢視數量:在 body 中以條件式進行篩選,會讓檢視依索引持續存活,因此請改在資料層篩選(在 Query 上使用 Predicate)。1
  • SwiftUI 提供三種著色器進入點(colorEffectdistortionEffectlayerEffect),能力依序遞增;只有 layerEffect 能取樣鄰近像素,而這正是模糊與域變形(domain warp)所需要的。3456
  • 著色器是無狀態的,因此動畫來自於把 TimelineView 的時間戳記當作參數餵入;影格之間不會帶有任何資訊。37
  • AppKit 與 UIKit 能透過 @Observable 取得自動重繪(不必再手動呼叫 needsDisplay),而 SwiftUI 可透過 NSHostingViewNSGestureRecognizerRepresentableNSHostingMenuNSHostingSceneRepresentation 嵌入既有的 App。8910

Lazy Stack 仰賴估算運作

Watch on Apple Developer ↗
UI Frameworks 工程師 Rens 說明,LazyVStack 由上而下進行佈局,一旦填滿可見區域便停止,其餘部分則以估算處理。

關於 lazy stack,首先要內化的一件事是:它是刻意以正確性換取效率。與 VStack 不同,LazyVStack 不會評估或繪製不可見的檢視;它由上而下佈局,一旦填滿可見區域便停止,並在檢視捲入時加入、捲出時移除。1 好處顯而易見,代價則較為微妙:由於 stack 從不載入所有檢視,螢幕外檢視的高度是依先前內容的平均值估算而來,理想寬度會縮減為第一個子檢視的寬度,而可見區域上方的空間本身也是近似值。1

這種估算並不是您繞過一次就好的 bug;它是其他每一項 lazy stack 決策所立足的基礎。議程 321 用一個方向變更的例子,把這個後果具體化。旋轉 iPhone,最頂端的可見檢視會維持錨定,但 stack 尚未測量其上方檢視的確切新佈局。捲回頂端時,stack 必須進行調和:它會修正可見區域上方的估算空間,並以相同的量更新捲動檢視的內容偏移量,使頂端的內容偏移量落在零。1 lazy stack 與包覆它的捲動檢視會精準地協調位置與偏移量,因此即便估算值更新,可見子檢視的相對位置也絕不會跳動。1

可付諸行動的推論,是一條關於該信任哪些捲動 API 的規則。由於絕對內容偏移量是估算的,讀取它(比方說以 .onScrollGeometryChange 在超過 100 點後隱藏一個按鈕)所得到的門檻,會隨著估算值穩定下來而漂移。2 穩定的訊號是相對可見性。.onScrollTargetVisibilityChange 修飾符會在捲動檢視中可見子檢視的集合改變時觸發,因此一個「捲動以展示」的按鈕可以把它的可見性綁定到螢幕上有哪些列,並搭配一個門檻(議程使用 80%),而非綁定到不穩定的像素數值。21 同樣的邏輯也適用於 .scrollTransition:一個把檢視推出其原始框架的變換,可能會讓 stack 誤以為某個可見檢視已在螢幕外而提早將其丟棄,因此任何捲動轉場都必須避免把原本不該可見的檢視推可見區域。111

為什麼您的 View 結構不等於子檢視

lazy stack 載入的子檢視,並不會與您撰寫的 view 結構一對一對應。一個 StepViewForEach 會解析成每個步驟一個 StepView,但如果每個 StepView 的 body 回傳兩個沒有外層佈局包覆的頂層檢視(一張圖示與一段說明),stack 便會分別載入它們。1 對 stack 而言重要的數字是解析後的子檢視數量,而非結構數量。

陷阱在於動態的子檢視數量。如果一個 StepView 會依據某個環境值回傳一個或零個子檢視,stack 便無法再信任索引,因為先前檢視的數量可能會改變。於是它會把較早的 StepView 實例保留下來以防萬一,這表示一個不相關的環境變更,可能會觸發已捲出螢幕的檢視重新評估 body,而 stack 也不會釋放它們的狀態。1 修正方法是把篩選從檢視移到資料上:如果您使用 SwiftData,請把條件放進 QueryPredicate 中,這樣不必建構任何檢視就能得知子檢視數量。1 在 body 中解包一個 optional 也有同樣讓檢視存活更久的效果;更乾淨的作法是在更上層顯示一個 ContentUnavailableView,而非讓 lazy stack 持有部分解析的列。1

預先擷取是讓估算感覺起來快速的機制。捲動時,捲動檢視只有到影格截止時間之前的時間,可以用來更新偏移量、繪製檢視,並執行您的偏移變更工作;如果顯示一個新檢視超出了這個預算,影格就會掉幀,您便會看到卡頓。1 為了避免這種情況,lazy stack 會檢查是否有多餘的時間,若有,便提早完成一個即將出現之檢視的部分工作(評估它的 body 與佈局,甚至把巢狀的 LazyHStack 分散到多個影格進行),如此一來,當檢視出現時,大部分工作都已完成。1 這正是議程之所以對 onAppear 如此強調的原因:如果您在 onAppear 中設定檢視,便會丟棄預先擷取的工作,並在它出現時被迫重做,有時還會拉進比所需更多的檢視,導致捲動變差。請在初始化器中設定檢視,讓它在出現時便處於合理狀態,並把 onAppear 保留給真正與出現時機相關的工作,例如在無限捲動中擷取下一頁。1 在反向捲動時,body 甚至可能會在預先擷取期間執行,而 onAppear 卻完全不會觸發。1

進階圖形不過是一條管線

Watch on Apple Developer ↗
UI Frameworks 工程師 Haotian 把進階效果定義為一連串標準管道彼此串接:「進階」在於組合的方式,而非單一 API 的複雜度。

議程 322 把「進階圖形」重新定義為組合。每個 SwiftUI 修飾符都是一根管道,接收資料、轉換它,再往下傳遞;進階的成果在於您如何串接這些管道,而不在任何單一複雜的 API。3 議程透過串接一連串平凡的階段,建構出一個 Apple Music 風格的即時歌詞檢視:把封面模糊化使其後退、在其上執行一個著色器、用時間驅動該著色器,並讓歌詞捲動同步到同一個時間來源。3

著色器階段才是真正需要抉擇之處。SwiftUI 透過三個能力遞增的效果進入點呼叫 Metal 著色器函式。colorEffect 在給定像素位置與原始顏色的情況下轉換每個像素的顏色,這對於灰階轉換之類的需求便已足夠。4 distortionEffect 則是把一個位置映射到另一個位置(您告訴 SwiftUI 從那個位置取樣此位置的顏色),用來處理不涉及顏色的幾何扭曲。5 layerEffect 最為靈活:它把整個檢視的圖層交給著色器,因此輸出像素可以取樣鄰近像素或整個區域,而這正是模糊與更豐富的扭曲所需要的。63

議程的域變形背景使用 layerEffect。一個均勻的 float2 偏移會把每個像素位移相同的量,這只會讓影像滑動;有機的動態需要逐像素的變化,因此著色器會取樣一張預先計算好的 NoiseTexture(以影像形式傳入,在 Metal 端以 texture2d 形式抵達),其紅色與綠色通道在每個 UV 座標上提供不同的 X 與 Y 偏移。3 取樣雜訊一次會扭曲影像;取樣兩次(第二次取樣的位置由第一次取樣位移而來),則會產生流動的團塊。這種二階技巧便是域變形,議程並指出其可下載的範例 App 附有可即時預覽參數的功能。3

有兩項框架事實讓這個動畫得以運作。著色器是無狀態的:它們不保留前一影格的任何記憶,輸出只取決於您傳入的參數。3 因此動態不能來自著色器內部,必須餵進去,而 TimelineView 便是供應它的管道,會在動畫排程上每影格觸發一次並帶有時間戳記。73 把那個時間戳記傳入著色器,加到雜訊取樣位置上,圖樣便會流動起來。歌詞那一側則從另一個方向重用同一個時間來源:播放時間戳記挑出當前歌詞行(粗體且清晰,其餘則淡化),而一個 onChange 會在時間推進時讓該行保持置中。123 作用中歌詞行上浮動的時間戳記,並非以 offset(那需要兩個檢視的尺寸)定位,而是以一個對齊輔助線(alignment-guide)覆寫來定位,從語意上重新定義一個對齊點,使子檢視的頂緣附著到其容器的底緣,而不需要手動偏移。133

SwiftUI 嵌入 AppKit 或 UIKit App

Watch on Apple Developer ↗
UI Frameworks 工程師 David Nadoba 指出,SwiftUI 從一開始就被設計為能與 AppKit 和 UIKit 並肩運作,就如同 Swift 被設計為能與 Objective-C 協作一樣。

互通議程一開始就拋出一個重新框定整個採用問題的觀點:大多數 App 其實早已隱含地使用了 SwiftUI。在新的設計中,NSSliderNSSwitchNSSegmentedControl 等 AppKit 控制項,底層都是以 SwiftUI 繪製,而 Liquid Glass 也同樣透過 SwiftUI 在各框架間共享其大部分的實作。8 因此「採用 SwiftUI」與其說是重寫,不如說是一個關於在何處把接縫明確化的決定。

第一步根本不需要任何 SwiftUI。AppKit 與 UIKit 現在會自動觀察 @Observable 型別:把一個模型類別標記為 @Observable,在像 drawKnob 這樣的繪製方法中讀取它的屬性,AppKit 便會追蹤每一次存取,並在任何被存取的屬性改變時重繪,從此不必再撰寫那種每當一個滑桿的值影響另一個外觀時就要手動寫的 needsDisplay = true814 觀察機制不只延伸到 draw(_:),也涵蓋 updateConstraints()layout()updateLayer() 與對應的 NSViewController 方法,而 UIKit 更進一步觸及 UIButtonUICollectionViewCell 等等。8 它在 2026 版本中預設啟用,並可透過 Info.plist 回溯部署至 macOS 15(NSObservationTrackingEnabled)與 iOS 18(UIObservationTrackingEnabled)。8

一旦模型成為 @Observable,真正的 SwiftUI 接縫就很小了。議程把一個以滑桿為基礎的調色器,重建為一個以 Canvas 繪製的圓形 SwiftUI 控制項(Canvas 是一個類似 drawRect 的即時模式 API,並可用 withCGContext 重用既有的 Core Graphics 程式碼),重用的正是同一個 @Observable ColorModel158 要把它嵌入到 AppKit 預期是檢視之處,只需用 NSHostingView(一個 NSView 的子類別)包起來;由於模型本身已驅動更新,這層包覆就是所需的全部。168 既有的手勢程式碼無需重寫即可沿用:一個 ForceClickGestureRecognizer 透過 NSGestureRecognizerRepresentable(實作 makeNSGestureRecognizerhandleNSGestureRecognizerAction)抵達 SwiftUI 檢視,再以一般的 .gesture 修飾符附加,並與 SwiftUI 自身的拖曳手勢共存。178 同一個 representable 家族還包含 NSViewRepresentable,用於朝另一個方向嵌入 NSView8

這道接縫可向上延伸至選單與場景。一個持有 ButtonPicker 的 SwiftUI View,可透過 NSHostingMenu(一個 NSMenu 子類別)變成一個真正的選單,設定為加入主選單之 NSMenuItem 的子選單,並以 keyboardShortcut 為該動作提供一條非手勢的路徑,給無法 force-click 的輸入裝置使用。188 完整的 SwiftUI 場景也能附加:一個 MenuBarExtra 透過 NSHostingSceneRepresentation 抵達既有的 App,在 applicationWillFinishLaunching 中以 addSceneRepresentation 加入,並以一個 Settings 場景的 Toggle 控制是否插入該額外項目,而 openSettings() 環境動作則可從 @IBAction 開啟設定。198 議程的結語才是真正具承載力的一點:它涵蓋的每一個 API 都在 2026 版本或更早就已推出,而且並不期待一個 App 必須完全是 SwiftUI 才能受惠。8 不過在這個週期中,現代化的推力在別處有更硬的一面:iOS 27 把 UIKit 以場景為基礎的生命週期列為啟動的必要條件,因此一個您用最新 SDK 重建、卻從未採用場景的 App,將會無法啟動。

SwiftUI 團隊在實驗室裡補充了什麼

來自 WWDC26 UI Frameworks 團隊實驗室(group lab)的兩項澄清,讓這幾場議程所描述的失效(invalidation)模型更加清晰。兩者都是依據本地轉錄的錄音改寫;Apple 並未為這些實驗室發布官方字幕,這與貫穿 Swift 團隊自身 Group Lab(語言工程師在那裡回答了並行與藍圖相關問題)的那道字幕缺口如出一轍。

把程式碼從 body 移到一個計算屬性中,並不會帶來任何失效上的好處。SwiftUI 仍會在每次重新執行 body 時重新執行該屬性,因此這個搬移純粹是可讀性的提升,僅此而已。20 效能的分界出現在更上一層:抽取出一個獨立的 view 型別,SwiftUI 便能獨立地使其失效,只在其輸入改變時重新執行該檢視,而非整個外層的 body。當您希望 SwiftUI 做更少的工作時,請去找一個新的檢視,而不是一個新的屬性。

每一次環境變更,都會使所有讀取該環境值的檢視失效。讀取環境很便宜;環境的頻繁變動則不然。21 請讓快速變動的值遠離環境(小組舉的例子是當前時間),因為一個每影格都更新的值,會在每次變動時把每一個讀取者都拖進重新評估之中。把易變的值沿著真正需要它的路徑往下傳,並讓環境承載那些保持靜止的東西。

稍後的一場實驗室議程又補充了三項機制細節,全部依據 WWDC 2026 SwiftUI Group Lab 本地轉錄的錄音改寫,Apple 並未為其發布官方字幕。

部分圖評估(partial graph evaluation)說明了預先擷取的工作究竟去了哪裡。小組描述了一個 lazy stack 如何在當前影格繪製完成後剩餘的影格時間裡,評估即將出現之儲存格的 body,然後在下一個影格開始前恰好停止。22 他們點出的陷阱是:一個會重新設定儲存格、使其因尺寸改變而必須重新佈局的 onAppear,會把那些預先擷取的工作丟棄。他們的建議是在儲存格的初始化器中完成尺寸相關的工作,而非在 bodyonAppear 中,這樣預先擷取的成果才能存活。22

互通的心智模型來自一位在 UIKit 上耕耘超過十年的小組成員。UIKit 由上而下佈局,從視窗向內推進至葉節點,而 SwiftUI 則由下而上建構,從最內層的節點向外擴展。22 當兩者交錯時,小組把這種交替分層的排列稱為三明治或蛋糕,這也是為什麼深度交錯會變得微妙:每個框架都想從相反的一端驅動佈局流程。22

上述關於環境的論點得到了一個生動的真實案例。當被問到他們見過最糟的環境誤用時,小組舉出把捲動位置放進環境的例子,因為它在捲動時每影格都會更新,於是每影格都使每一個讀取者失效。22

第二場 SwiftUI Group Lab 議程又補充了四項機制細節,全部依據 WWDC 2026 SwiftUI Group Lab(第 2 場)本地轉錄的錄音改寫;Apple 並未為這些實驗室發布官方字幕。

onGeometryChange(for:of:action:) 修飾符實際讀起來比聽起來更合理,原因在於它的兩個閉包如何分工。transform 閉包會在每影格以即時幾何資訊執行,但只有它回傳的值才會決定動作是否觸發,因為結果型別是 Equatable,而動作只在該值改變時才執行。23 因此回傳一個粗略的值(一個尺寸分級或一個佈局斷點,而非原始尺寸),便能把一個影格率的訊號轉換成只在門檻處觸發兩次、而非持續觸發的訊號。小組同時警告,GeometryReader 對它所包覆的子檢視而言成本高昂,應將其限縮在一個背景中,使其只測量而不驅動主要佈局。23

一個好的 dynamic property 幾乎可以直接取代大多數的 onChange 工作。一個 DynamicPropertyupdate() 方法會在檢視的 body 之前立即執行,因此一個自訂的屬性包裝器可以在那個時間點同步地提供一個已快取的值(小組的例子是一張影像),從而省去 onAppear 的往返以及它所觸發的重新繪製。24 小組的說法是,onChange 的大多數用途都可以用一個建構良好的 dynamic property 取代。24

一個在 ScrollView 內繪製超出其回報佈局邊界的檢視,可能會被剔除(culled),因為系統是依據它被告知的邊界、而非它實際繪製的位置來判定它是否在螢幕外。25 小組把這點指為自訂下拉選單與 overlay 溢出其宿主檢視後在捲動途中消失的具體故障原因,並指出同樣的危害也適用於延伸超出其錨點的 overlay 內容。25

要呈現一個凌駕一切(包括 sheet)之上的全螢幕 overlay,在純 SwiftUI 中並沒有乾淨的答案。小組的建議是透過 UIKit 的場景生命週期降到一個新的 UIWindow,其更深層的原則是:最後一個視窗勝出,且對於什麼位於最上層必須有單一的事實來源。26 他們補充說,更好的修正往往是還原使用者所預期的導覽堆疊,而不是把一個帶有干擾性的封面拋到一切之上。26

該先採用什麼

一個以機制方式來解釋的版本,會獎勵依槓桿效益(而非新鮮感)來排序的作法。

  1. 在您的 AppKit/UIKit 程式碼中,把模型類別切換為 @Observable 它能刪除手動的 needsDisplay 呼叫,讓每一個觀察中的繪製與佈局方法都取得自動重繪,並且是讓日後 NSHostingView 的嵌入變得輕而易舉的前提。814 這是三場議程中風險最低、立即回報最高的一步。
  2. 稽核 lazy stack 是否有動態的子檢視數量。 任何在 body 中有條件地回傳零個或一個子檢視、或解包一個 optional 的 ForEach 葉節點,都在依索引讓檢視持續存活;把篩選移到 Query 上的一個 Predicate(或移到階層中更高處),記憶體與捲動至項目的效能都會改善。1
  3. 把檢視設定從 onAppear 移到初始化器中。 預先擷取只有在預先擷取的工作能存活時才有幫助;在 onAppear 中變動尺寸或內容的設定會把那些工作丟棄。1 這是一個安靜而廣泛的捲動流暢度勝利。
  4. 以相對可見性的 API 取代絕對捲動偏移量的讀取。 任何綁定到內容偏移量的東西都會漂移;.onScrollTargetVisibilityChange 綁定的是哪些列實際可見。21
  5. 只在一個小效果確實值得時才動用著色器。colorEffectdistortionEffect 開始;只有在一個效果必須取樣鄰近像素時才升級到 layerEffect,並以一個 TimelineView 的時間戳記驅動任何動態,而不要期望著色器內部會有狀態。4567

貫穿這三者的主線是:在向框架施壓之前,先預測它。lazy stack 會估算、著色器會遺忘,而互通是一道接縫,不是一次重寫。帶著這三項事實去建構,其餘的便會水到渠成。

常見問題

為什麼我的 SwiftUI lazy stack 捲動位置會跳動或漂移?

因為 LazyVStack 不會載入螢幕外的檢視,它會估算它們的高度以及可見區域上方的空間,所以絕對內容偏移量是一個估算值,框架會在它逐漸得知真實佈局時加以修正(例如在方向變更之後,當您捲回頂端時它會調和該估算值)。1 如果您把 UI 綁定到絕對偏移量,門檻便會隨著估算值穩定下來而漂移。請改用 .onScrollTargetVisibilityChange,它是依據哪些子檢視實際可見而觸發。2

我該如何在 iOS 27 的 SwiftUI lazy stack 中維持捲動流暢?

讓預先擷取發揮作用:在初始化器中設定檢視,這樣 lazy stack 在檢視出現前所完成的工作才不會被丟棄,並避免在 onAppear 中變動檢視的尺寸或內容。1 同時避免在 ForEach 葉節點中使用動態變動的子檢視數量,並避免在檢視出現後進行佈局變更(例如由 onGeometryChange 驅動的高度),因為兩者都會迫使 stack 在捲動途中重做工作或重新計算位置。1

我什麼時候該用 colorEffectdistortionEffectlayerEffect

當您要從像素的位置與原始顏色轉換每個像素的顏色時(例如灰階濾鏡),使用 colorEffect4 對於幾何效果,使用 distortionEffect,您在其中把一個輸出位置映射到一個要取樣的來源位置。5 當輸出像素取決於不只一個輸入像素時,使用 layerEffect,因為它把整個檢視圖層交給著色器去取樣鄰近像素或整個區域,而這正是模糊與域變形所需要的。6

我該如何在 SwiftUI 中讓 Metal 著色器產生動畫?

著色器是無狀態的:它們不保留前一影格的任何記憶,只取決於其參數,因此您無法從著色器內部產生動畫。3 請餵入一個隨時間改變的值。一個在動畫排程上的 TimelineView 會每影格觸發一次並帶有時間戳記;把那個時間戳記當作參數傳入著色器(議程把它加到雜訊取樣位置上),效果便會產生動畫。73

我可以在不重寫的情況下,把 SwiftUI 加進一個既有的 AppKit 或 UIKit App 嗎?

可以,而且議程明確指出,沒有任何 App 必須完全是 SwiftUI 才能受惠。8 把您的模型標記為 @Observable,讓 AppKit 與 UIKit 自動重繪,接著以 NSHostingView 嵌入 SwiftUI 檢視、用 NSGestureRecognizerRepresentable 把既有的手勢辨識器帶過來、用 NSHostingMenu 建構選單,並從您的 app delegate 以 NSHostingSceneRepresentation 附加 SwiftUI 場景;以上全都在 2026 版本或更早就已推出。816171819

完整的 Apple Ecosystem 系列:SwiftUI 基底(result builder、不透明型別、值型別的檢視樹)解釋了為什麼一個 lazy stack 會把 view 結構解析成另一組子檢視;iOS 27 SwiftUI 表層(重新排序、文件、工具列、錯誤)與這篇效能與互通的故事並列;@Observable 內部機制 如今也驅動著 AppKit 與 UIKit 的自動重繪;以及 Liquid Glass 模式,互通議程把其跨框架的實作歸功於共享的 SwiftUI。樞紐是 Apple Ecosystem 系列。若想了解更廣泛的 iOS 與 AI agent 脈絡,請參閱 iOS Agent 開發指南

參考資料


  1. 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 onAppear versus initializer setup. 

  2. 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. 

  3. 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 NoiseTexture domain-warp technique, stateless shaders driven by time, and alignment-guide attachment. 

  4. Apple Developer Documentation: colorEffect(_:isEnabled:). Returns a new view that applies a shader transforming each pixel’s color, given its position and original color. 

  5. 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. 

  6. 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. 

  7. 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. 

  8. Apple, WWDC26 session 272, “Use SwiftUI with AppKit and UIKit.” developer.apple.com/videos/play/wwdc2026/272. Covers automatic @Observable redraw in AppKit/UIKit, observation back-deployment via Info.plist, Canvas, NSHostingView, NSGestureRecognizerRepresentable, NSHostingMenu, and NSHostingSceneRepresentation

  9. Apple Developer Documentation: NSGestureRecognizerRepresentable. A protocol that wraps an NSGestureRecognizer for use as a SwiftUI gesture, implemented with makeNSGestureRecognizer and handleNSGestureRecognizerAction

  10. Apple Developer Documentation: MenuBarExtra. A scene that renders a menu bar item; session 272 attaches it to an existing AppKit app through NSHostingSceneRepresentation

  11. 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. 

  12. Apple Developer Documentation: onChange(of:initial:_:). Runs an action when a value changes; session 322 uses it to recenter the current transcript line. 

  13. 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. 

  14. 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. 

  15. Apple Developer Documentation: Canvas. An immediate-mode drawing view whose closure receives a GraphicsContext; session 272 uses it to redraw the circular color picker and notes withCGContext for reusing Core Graphics code. 

  16. Apple Developer Documentation: NSHostingView. An NSView subclass that hosts a SwiftUI view hierarchy inside an AppKit view tree. 

  17. Apple Developer Documentation: NSViewRepresentable. A wrapper that lets an NSView participate in a SwiftUI view hierarchy; session 272 names it alongside NSGestureRecognizerRepresentable as part of the representable family. 

  18. Apple Developer Documentation: NSHostingMenu. An NSMenu subclass that renders a SwiftUI view as menu content, added to the main menu as the submenu of an NSMenuItem

  19. 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. 

  20. 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 body into a computed property is a readability change only, and that the independent-invalidation boundary appears when you extract a separate view type. 

  21. 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. 

  22. 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. 

  23. 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 onGeometryChange transform-gates-action mechanism (return a coarse value to fire at thresholds) and the guidance to confine an expensive GeometryReader to a background. The two-closure shape is documented at onGeometryChange(for:of:action:): the of: transform closure derives an Equatable value from the geometry proxy and the action: closure runs only when that value changes. 

  24. 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 onChange work with a dynamic property that vends a cached value synchronously. Apple’s DynamicProperty protocol defines an update() method that SwiftUI calls immediately before rendering a view’s body so the property holds its most recent value. 

  25. 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 to overlay content extending past its anchor. 

  26. 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 UIWindow through 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. 

相關文章

iOS 27 的無障礙設計:閱讀類 App 與自訂控制項

iOS 27 針對閱讀類 App 與自訂控制項的無障礙設計:文字導覽連結、causesPageTurn、UITextInput、adjustable 特徵與直接觸控。

2 分鐘閱讀

iOS 27 的 SwiftUI 有哪些新功能

iOS 27 重新打造了 SwiftUI 的清單、文件、工具列與錯誤處理:拖曳重新排序、可讀寫的文件模型、工具列溢位,以及以項目為基礎的警示。

10 分鐘閱讀

從76到100:達成完美的Lighthouse分數

一個個人作品集網站如何從行動裝置Lighthouse效能分數76分、CLS 0.493,進步到全類別完美的100/100/100/100。

3 分鐘閱讀