Joe's Blog

iOS 開發筆記

WWDC26 What's New in SwiftUI

發佈於 2026-06-24

SwiftUI 今年沒有顛覆性新概念,但散落的小改動加起來很可觀。這篇整理 What’s New in SwiftUI session 的重點。


🎨 Liquid Glass 自動適配

升上 iOS 27 / macOS 27 後,App 不需要改 code 就會自動套用新的 Liquid Glass 外觀——前提是你沒有大量自製 UI。

幾個搭配的新環境值和行為:


🛠 Toolbar 強化

四個新東西,組合起來很實用:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
StickerPageView()
    .toolbar {
        ToolbarItemGroup {
            UndoButton()
            RedoButton()
        }
        .visibilityPriority(.high)  // 空間不夠時優先保留

        ToolbarOverflowMenu {        // 次要操作收進去
            ChoosePhotoButton()
            ExportAsImageButton()
        }

        ToolbarItem(placement: .topBarPinnedTrailing) {
            ShareButton()             // 永遠釘在右上角
        }
    }

捲動時自動收起:

1
2
ScrollView { ... }
    .toolbarMinimizeBehavior(.onScrollDown, for: .navigationBar)

以前要做這些效果都得自己土法煉鋼,現在系統內建。


📋 .reorderable().swipeActions() 通用化

過去這兩個 modifier 都只能用在 List 上,現在解放到任何容器。

Reorderable(拖曳排序)

1
2
3
4
5
6
7
8
9
LazyVGrid {
    ForEach(stickers) { sticker in
        StickerListItemView(sticker: sticker)
    }
    .reorderable()
}
.reorderContainer(for: Sticker.self) { difference in
    difference.apply(to: &stickers)
}

watchOS 也是第一次支援。SwiftUI 處理拖曳動畫和插入位置預覽,你只負責把 difference 套用回資料源。

Swipe Actions

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
ScrollView {
    LazyVStack {
        ForEach(stickers) { sticker in
            StickerListItemView(sticker: sticker)
                .swipeActions {
                    DeleteButton(sticker: sticker)
                }
        }
    }
}
.swipeActionsContainer()

swipeActionsContainer() 加在 ScrollView 上,系統會自動處理「只能同時開一列」、「捲動關閉」這些行為。


🖼️ AsyncImage 升級

兩個一直以來的痛點終於解決。

自動 HTTP cache

1
AsyncImage(url: imageURL)

不改一行 code,就會遵循 server 的 cache header 做快取。以前要自己包一層 URLSession + URLCache,現在內建。

支援 URLRequest 和自訂 URLSession

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
@Observable class StickerStore {
    static let imageSession: URLSession = {
        let config = URLSessionConfiguration.default
        config.urlCache = URLCache(memoryCapacity: 64 * 1024 * 1024,
                                   diskCapacity: 256 * 1024 * 1024)
        return URLSession(configuration: config)
    }()
}

ForEach(pets) { pet in
    AsyncImage(request: URLRequest(
        url: pet.imageURL,
        cachePolicy: .returnCacheDataElseLoad
    ))
}
.asyncImageURLSession(StickerStore.imageSession)

URLRequest 可以帶 header(authenticated endpoint)、客製 timeout 和 cache policy;asyncImageURLSession modifier 套用到整個 view hierarchy。


⚠️ @State 延遲初始化(會打到舊 code)

這次 SwiftUI 最重要的改動,而且會讓你既有專案編譯失敗

改動

1
2
3
4
5
6
7
@Observable class StickerStore { }

struct StickerStoreView: View {
    @State private var store = StickerStore()
    // 現在是「lazy 初始化」——只在 view 第一次需要時建立一次
    // 以前每次 view 重新 init 都會建立臨時的 StickerStore 再丟掉
}

效能改善很大,而且回填到 iOS 17、macOS 14 等舊版本

Breaking change

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// ❌ 舊寫法,現在直接編譯錯誤
struct StickerPageView: View {
    @State private var page = StickerPage()

    init(title: String) {
        self.page = StickerPage(title: title)  // Error!
    }
}

// ✅ 正確寫法:拿掉預設值
struct StickerPageView: View {
    @State private var page: StickerPage

    init(title: String) {
        self.page = StickerPage(title: title)
    }
}

升版前先 grep 一下專案裡 init 中對 @State 的賦值,提早處理。詳見 TN3211


🏗️ ContentBuilder

ViewBuilder 的進化版,多種 builder 統一成 ContentBuilder

1
2
@ContentBuilder
func stickerLibraryView() -> some View { ... }

對使用者最直接的影響是編譯速度——複雜 view hierarchy 在 Xcode 27 build 起來明顯比較快。不指定 deployment target 都能用。


📑 Document API 大改

WritableDocument / ReadableDocument 取代舊的 FileDocument,主要強化:

如果你有寫 document-based App,這次是值得重做的程度。


🎯 其他小改動

Prominent Tab role

1
2
3
Tab(role: .prominent) {
    CartTab()  // 視覺上跟其他 tab 區隔(如購物車、通知)
}

Confirmation dialog 支援 item binding

1
2
3
4
5
@State private var stickerToDelete: Sticker?

.confirmationDialog("Delete?", item: $stickerToDelete) { sticker in
    Button("Delete", role: .destructive) { /* delete */ }
}

.sheet(item:) 寫法一致,比之前用 isPresented + 額外 state 乾淨很多。.alert() 也有同款 overload。


🔚 總結

這次 SwiftUI 沒有概念性的大轉折,但該補的洞補了不少

最值得提早處理的是 @State init 賦值會編譯錯誤,其他都是無痛升級。


What’s New in SwiftUI — WWDC26 Session 269


本文使用 Claude 共同完成