Highlight
SwiftUI 在 2027 平台版本中引入了全新的 Document API(支持直接磁盘访问和快照差异写入)、可重排序容器、Toolbar 可见性优先级与自动最小化、任意视图的滑动手势,以及
@State宏化和ContentBuilder带来的编译性能提升。
核心内容
Liquid Glass 与窗口状态响应
在 2027 版本中,SwiftUI 应用自动获得 Liquid Glass 的更新外观,无需修改任何代码。(02:17)macOS 上的 Liquid Glass 自定义元素可以标记为 interactive,对鼠标点击的响应更加流畅。iPad 应用在窗口非活跃时,图标和文字会自动变暗,帮助用户识别当前活跃窗口。
开发者可以通过 appearsActive 环境值精确控制自定义视图的非活跃状态表现:
struct SidebarFooterView: View {
@Environment(\.appearsActive) private var appearsActive
var body: some View {
MyAccountView()
.opacity(appearsActive ? 1 : 0.5)
}
}
关键点:
appearsActive是 2027 新增的环境值- 当窗口非活跃时返回
false,活跃时返回true - 用于自定义侧边栏、工具栏等元素的视觉反馈
iPad 和 Mac 的菜单栏默认只显示关键操作的图标。通过 .labelStyle(.titleAndIcon) 可以让特定菜单项同时显示图标和文字,使其更醒目。(03:34)
Toolbar 自适应与可见性控制
随着功能增加,Toolbar 的按钮越来越多。在 iPhone 或窗口较小时,系统会自动隐藏放不下的按钮。2027 版本提供了三个新 API 来解决这个问题。
可见性优先级:通过 visibilityPriority(.high) 让重要按钮在空间不足时优先保留。
StickerPageView()
.toolbar {
ToolbarItemGroup {
UndoButton()
RedoButton()
}
.visibilityPriority(.high)
ToolbarOverflowMenu {
ChoosePhotoButton()
ExportAsImageButton()
ClearAllStickersButton()
}
ToolbarItem(placement: .topBarPinnedTrailing) {
ShareButton()
}
}
关键点:
visibilityPriority(.high)标记 Undo/Redo 为重要操作,优先保留可见ToolbarOverflowMenu将不常用的按钮固定放入溢出菜单.topBarPinnedTrailing让 Share 按钮始终显示在右侧,不会被隐藏
自动最小化:当用户向下滚动时,导航栏自动收起,为内容留出更多空间。(07:37)
ScrollView {
StickerListView()
}
.toolbarMinimizeBehavior(.onScrollDown, for: .navigationBar)
全新的 Document API
SwiftUI 原有的 FileDocument 和 ReferenceFileDocument 协议在 2027 版本上得到了大幅扩展。新 API 提供了文档创建上下文、磁盘读写性能优化,以及直接访问文档 URL 的能力。(08:12)
文档创建源:应用启动时可以提供多种创建文档的方式。
@main
struct Stickers: App {
var body: some Scene {
DocumentGroupLaunchScene("Create a Sticker Page") {
NewDocumentButton("New Sticker Page", source: .blank)
NewDocumentButton("Sticker Page from Photo…", source: .photo)
}
DocumentGroup { document in
StickerPageDocumentView(document)
} { configuration, context in
StickerPageDocument(configuration: configuration, context: context)
}
}
}
extension DocumentCreationSource {
static let blank = Self(id: "blank")
static let photo = Self(id: "photo")
}
关键点:
DocumentGroupLaunchScene定义启动场景中的新建文档按钮DocumentCreationSource声明不同的创建来源(空白、从照片等)- 创建闭包通过
context参数接收来源信息,初始化器据此决定行为
写入优化:新协议 WritableDocument 要求三个部分:可写入格式列表、快照方法、Writer。(11:25)
@Observable
final class StickerDocument {
static let writableDocumentTypes: [UTType] = [.stickerDocument]
@MainActor
func snapshot(contentType: UTType) async throws -> sending PageSnapshot {
makeSnapshot()
}
func writer(configuration: sending WriteConfiguration) -> sending Writer {
Writer(contentType: configuration.contentType)
}
}
struct Writer<Snapshot>: DocumentWriter {
typealias Snapshot = PageSnapshot
let contentType: UTType
nonisolated func write(
snapshot: sending PageSnapshot, to destination: URL,
previous: sending PageSnapshot?, progress: consuming Subprogress
) async throws {
// 对比 current 和 previous snapshot,只写入变化的部分
// 使用 Subprogress 报告写入进度
}
}
关键点:
snapshot()返回文档某一时刻的完整状态,用sending保证线程安全Writer的write方法是nonisolated且异步的,磁盘写入在后台执行- 通过对比
snapshot和previous实现增量写入 Subprogress用于向系统报告保存进度
多格式导出:同一个 Writer 可以支持多种输出格式。(14:35)
@Observable
final class StickerDocument: WritableDocument {
static let writableContentTypes: [UTType] = [.stickerDocument, .png]
}
// 在 write 方法中根据 contentType 分发
if contentType.conforms(to: .stickerDocument) {
// 写入自定义包格式
} else if contentType.conforms(to: .png) {
let context = CGContext(/* ... */)
context.draw(/* ... */)
}
读取协议:ReadableDocument 与 WritableDocument 对称,配合 DocumentReader 完成磁盘读取。(13:27)
可重排序容器
2027 版本引入了 Reorderable API,支持在 List、Grid 等任意容器中拖拽重排。(15:58)
List {
ForEach(stickers) { sticker in
StickerListItemView(sticker: sticker)
}
.reorderable()
}
.reorderContainer(for: Sticker.self) { difference in
difference.apply(to: &stickers)
}
同样的代码可以直接用于 LazyVGrid,无需修改重排逻辑:
LazyVGrid {
ForEach(stickers) { sticker in
StickerListItemView(sticker: sticker)
}
.reorderable()
}
.reorderContainer(for: Sticker.self) { difference in
difference.apply(to: &stickers)
}
关键点:
.reorderable()修饰ForEach,启用拖拽交互.reorderContainer(for:)修饰外层容器,协调重排手势difference.apply使用 swift-collections 的OrderedDictionary提交排序变更- 首次支持 watchOS
任意视图的滑动手势
滑动手势不再仅限于 List,现在可以应用到任何视图。(18:15)
ScrollView {
LazyVStack {
ForEach(stickers) { sticker in
StickerListItemView(sticker: sticker)
.swipeActions {
DeleteButton(sticker: sticker)
}
}
}
}
.swipeActionsContainer()
关键点:
.swipeActions()修饰单个列表项.swipeActionsContainer()修饰外层ScrollView,协调所有子项的滑动手势
Confirmation Dialog 与 Alert 的 item 绑定
confirmationDialog 和 alert 现在支持与 sheet 相同的 item 绑定模式。(18:54)
struct StickerCanvasView: View {
var stickers: [Sticker]
@State private var stickerToDelete: Sticker?
var body: some View {
ZStack {
ForEach(stickers) { sticker in
PlacedStickerView(sticker: sticker)
.contextMenu {
Button("Delete") { stickerToDelete = sticker }
}
}
}
.confirmationDialog("Delete?", item: $stickerToDelete) { sticker in
DeleteStickerButton(sticker)
}
}
}
关键点:
item: $stickerToDelete绑定一个可选值- 当值非 nil 时弹窗自动出现,nil 时自动消失
alert也支持相同的 item 绑定模式
详细内容
AsyncImage 缓存改进
以前 AsyncImage 不会在内存中保留已加载的图片。向上滚动后再向下,图片会重新加载。2027 版本默认启用了标准 HTTP 缓存,无需修改代码。(20:57)
如果需要更精细的控制,可以传入自定义的 URLRequest 和 URLSession:
@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允许设置缓存策略、请求头等asyncImageURLSession修饰符设置全局的 URLSession,适合长期配置- 默认缓存自动生效,无需额外代码
@State 宏化与惰性初始化
@State 在 2027 版本从属性包装器变成了宏(macro)。最大的变化是:@State 修饰的 Observable 类现在只会初始化一次。(23:08)
@Observable class StickerStore { }
struct StickerStoreView: View {
@State private var store = StickerStore()
var body: some View {
// store 只在首次初始化时创建,父视图更新不会重复创建
}
}
这个行为已回溯到 iOS 17、macOS 14 及对应版本。
迁移注意:如果 @State 变量有默认值,又在 init 中赋值,会产生编译错误。(23:48)
// 错误:Variable 'self.title' used before being initialized
struct StickerPageView: View {
@State private var page = StickerPage()
let title: String
init(title: String) {
self.page = StickerPage(title: title)
self.title = title
}
}
// 修正:去掉默认值
struct StickerPageView: View {
@State private var page: StickerPage
let title: String
init(title: String) {
self.page = StickerPage(title: title)
self.title = title
}
}
ContentBuilder 编译性能优化
深层嵌套的 SwiftUI 视图常常触发编译器错误:“The compiler is unable to type-check this expression in reasonable time”。(24:31)
原因是 Section、Group、ForEach 等容器各有多个初始化器重载,编译器需要尝试所有组合才能确定类型。2027 版本将这些常用 builder 统一为 ContentBuilder,消除了大部分类型检查分支。(26:07)
@ContentBuilder
func stickerLibraryView() -> some View {
// ...
}
关键点:
ContentBuilder是ViewBuilder的演进,统一了多种 builder 类型- 编译性能提升适用于所有最低部署目标
- 使用 Xcode 27 构建时生效,无论目标平台是 2027 还是更早版本
核心启发
1. 用新 Document API 重构现有文档应用
如果你维护一个基于 FileDocument 的应用,迁移到新的 WritableDocument/ReadableDocument 协议可以获得增量写入、异步保存进度、多格式导出等能力。入口是 DocumentGroup 和 DocumentGroupLaunchScene。
2. 为 iPad/Mac 应用添加 Toolbar 自适应
用 visibilityPriority(.high) 标记核心操作,用 ToolbarOverflowMenu 收纳次要按钮,用 .topBarPinnedTrailing 固定分享按钮。配合 .toolbarMinimizeBehavior(.onScrollDown) 在滚动时自动收起导航栏,提升内容阅读体验。
3. 用 Reorderable API 替代自定义拖拽排序
如果你之前用 onMove 或自定义 DragGesture 实现列表重排,现在可以用 .reorderable() + .reorderContainer() 替换。代码更简洁,自动获得动画和交互,且支持 List、Grid、LazyVGrid 等多种容器。
4. 为任意滚动视图添加滑动手势
不再局限于 List,LazyVStack、ScrollView 中的任意子视图都可以添加 .swipeActions()。只需在外层容器加 .swipeActionsContainer() 即可协调手势。
5. 检查 @State 宏化带来的编译错误
升级到 Xcode 27 后,检查项目中 @State 变量是否在 init 中重复赋值。修复方式是去掉属性声明处的默认值,只在 init 中赋值。
关联 Session
- Modernize your UIKit app — UIKit 与 SwiftUI 混用时的屏幕尺寸适配指南
- Build powerful drag and drop in SwiftUI — Reorderable API 的深入讲解和更多拖拽场景
- What’s new in SwiftData — 与 Document API 配合的数据持久化方案
- SwiftUI graphics and animations — 新图形和动画 API,可与 Document 应用中的视觉效果结合
- What’s new in Xcode — Xcode 27 中的 SwiftUI Specialist Skill 和 What’s New In SwiftUI Skill 使用指南
评论
GitHub Issues · utterances