axiom-swiftui-26-ref
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSwiftUI 26 Features
SwiftUI 26 新特性
Overview
概述
Comprehensive guide to new SwiftUI features in iOS 26, iPadOS 26, macOS Tahoe, watchOS 26, and visionOS 26. From the Liquid Glass design system to rich text editing, these enhancements make SwiftUI more powerful across all Apple platforms.
Core principle From low level performance improvements all the way up through the buttons in your user interface, there are some major improvements across the system.
本指南全面介绍iOS 26、iPadOS 26、macOS Tahoe、watchOS 26和visionOS 26中的SwiftUI新特性。从Liquid Glass设计系统到富文本编辑,这些增强功能让SwiftUI在所有Apple平台上更加强大。
核心原则 从底层性能优化到用户界面中的按钮,整个系统都有重大改进。
When to Use This Skill
何时使用本技能
- Adopting the Liquid Glass design system
- Implementing rich text editing with AttributedString
- Embedding web content with WebView
- Optimizing list and scrolling performance
- Using the @Animatable macro for custom animations
- Building 3D spatial layouts on visionOS
- Bridging SwiftUI scenes to UIKit/AppKit apps
- Implementing drag and drop with multiple items
- Creating 3D charts with Chart3D
- Adding widgets to visionOS or CarPlay
- Adding custom tick marks to sliders (chapter markers, value indicators)
- Constraining slider selection ranges with
enabledBounds - Customizing slider appearance (thumb visibility, current value labels)
- Creating sticky safe area bars with blur effects
- Opening URLs in in-app browser
- Using system-styled close and confirm buttons
- Applying glass button styles (iOS 26.1+)
- Controlling button sizing behavior
- Implementing compact search toolbars
- 采用Liquid Glass设计系统
- 使用AttributedString实现富文本编辑
- 嵌入WebView加载网页内容
- 优化列表与滚动性能
- 使用@Animatable宏实现自定义动画
- 在visionOS上构建3D空间布局
- 将SwiftUI场景桥接到UIKit/AppKit应用
- 实现多项目拖放功能
- 使用Chart3D创建3D图表
- 为visionOS或CarPlay添加小组件
- 为滑块添加自定义刻度标记(章节标记、值指示器)
- 使用约束滑块选择范围
enabledBounds - 自定义滑块外观(滑块 thumb 可见性、当前值标签)
- 创建带模糊效果的粘性安全区域栏
- 在应用内浏览器中打开URL
- 使用系统样式的关闭和确认按钮
- 应用玻璃按钮样式(iOS 26.1+)
- 控制按钮尺寸行为
- 实现紧凑搜索工具栏
System Requirements
系统要求
iOS 26+, iPadOS 26+, macOS Tahoe+, watchOS 26+, visionOS 26+
iOS 26+, iPadOS 26+, macOS Tahoe+, watchOS 26+, visionOS 26+
Liquid Glass Design System
Liquid Glass设计系统
For comprehensive Liquid Glass coverage, see
如需全面了解Liquid Glass,请参阅
- skill — Design principles, implementation, variants, design review pressure
axiom-liquid-glass - skill — App-wide adoption guide (app icons, controls, navigation, menus, windows)
axiom-liquid-glass-ref
- 技能——设计原则、实现方式、变体、设计评审要点
axiom-liquid-glass - 技能——全应用采用指南(应用图标、控件、导航、菜单、窗口)
axiom-liquid-glass-ref
Overview
概述
The new design system provides "a bright and fluid experience that's consistent across Apple platforms." Apps automatically adopt the new appearance upon recompilation - navigation containers, tab bars, and toolbars update automatically.
新设计系统提供“在所有Apple平台上一致的明亮流畅体验”。应用重新编译后会自动采用新外观——导航容器、标签栏和工具栏会自动更新。
Key visual elements
关键视觉元素
- Glassy sidebars on iPad/macOS that reflect surrounding content
- Compact tab bars on iPhone
- Liquid Glass toolbar items with morphing transitions
- Blur effects on scroll edges
- iPad/macOS上的玻璃质感侧边栏,可反射周围内容
- iPhone上的紧凑标签栏
- 带有变形过渡效果的Liquid Glass工具栏项
- 滚动边缘的模糊效果
Automatic Adoption
自动采用
swift
// No code changes required - recompile and get new design
NavigationSplitView {
List {
// Sidebar automatically gets glassy appearance on iPad/macOS
}
} detail: {
// Detail view
}
// Tab bars automatically compact on iPhone
TabView {
// Tabs get new appearance
}swift
// 无需修改代码——重新编译即可获得新设计
NavigationSplitView {
List {
// 侧边栏在iPad/macOS上自动获得玻璃质感外观
}
} detail: {
// 详情视图
}
// iPhone上的标签栏自动变为紧凑样式
TabView {
// 标签页获得新外观
}Toolbar Customization
工具栏自定义
Toolbar Spacer API
工具栏间隔器API
swift
.toolbar {
ToolbarItemGroup(placement: .topBarTrailing) {
Button("Up") { }
Button("Down") { }
// Fixed spacer separates button groups
ToolbarSpacer(.fixed)
Button("Settings") { }
}
}swift
.toolbar {
ToolbarItemGroup(placement: .topBarTrailing) {
Button("Up") { }
Button("Down") { }
// 固定间隔器分隔按钮组
ToolbarSpacer(.fixed)
Button("Settings") { }
}
}ToolbarItemGroup for Visual Grouping
用于视觉分组的ToolbarItemGroup
Items within a share a single Liquid Glass background, creating a visual "pill" for related actions:
ToolbarItemGroupswift
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Cancel", systemImage: "xmark") {}
}
ToolbarItemGroup(placement: .primaryAction) {
Button("Draw", systemImage: "pencil") {}
Button("Erase", systemImage: "eraser") {}
}
ToolbarSpacer(.flexible)
ToolbarItem(placement: .confirmationAction) {
Button("Save", systemImage: "checkmark") {}
}
}Key insight: now controls visual appearance, not just position. automatically applies styling; uses standard glass.
ToolbarItemPlacementconfirmationActionglassProminentcancellationActionToolbarItemGroupswift
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Cancel", systemImage: "xmark") {}
}
ToolbarItemGroup(placement: .primaryAction) {
Button("Draw", systemImage: "pencil") {}
Button("Erase", systemImage: "eraser") {}
}
ToolbarSpacer(.flexible)
ToolbarItem(placement: .confirmationAction) {
Button("Save", systemImage: "checkmark") {}
}
}核心要点:现在控制视觉外观,而不仅仅是位置。会自动应用样式;使用标准玻璃样式。
ToolbarItemPlacementconfirmationActionglassProminentcancellationActionProminent Tinted Buttons in Liquid Glass
Liquid Glass中的突出着色按钮
swift
Button("Add Trip") {
addTrip()
}
.buttonStyle(.borderedProminent)
.tint(.blue)
// Liquid Glass toolbars support tinting for prominenceToolbar items also support for notification counts:
.badge()swift
ToolbarItem(placement: .confirmationAction) {
Button("Done", systemImage: "checkmark") { }
.badge(3) // Badge count on glass toolbar item
}swift
Button("Add Trip") {
addTrip()
}
.buttonStyle(.borderedProminent)
.tint(.blue)
// Liquid Glass工具栏支持着色以突出显示工具栏项还支持显示通知计数:
.badge()swift
ToolbarItem(placement: .confirmationAction) {
Button("Done", systemImage: "checkmark") { }
.badge(3) // 玻璃工具栏项上的徽章计数
}Removing Items from Group Background
从组背景中移除项
Some toolbar items should appear without the shared group background, like an avatar or standalone icon. Apply to separate an item visually:
sharedBackgroundVisibility(.hidden)swift
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
// Avatar appears without glass background pill
Image(systemName: "person.crop.circle")
.sharedBackgroundVisibility(.hidden)
}
}某些工具栏项不应显示共享组背景,例如头像或独立图标。应用可在视觉上分离项:
sharedBackgroundVisibility(.hidden)swift
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
// 头像显示时无玻璃背景胶囊
Image(systemName: "person.crop.circle")
.sharedBackgroundVisibility(.hidden)
}
}Monochrome Icon Rendering
单色图标渲染
Icons use monochrome rendering in more places, including toolbars. This reduces visual noise, emphasizes content, and maintains legibility. You can still tint icons with , but use it to convey meaning (call to action, next step) — not just for visual effect.
.tint()图标在更多地方使用单色渲染,包括工具栏。这减少了视觉噪音,突出了内容,并保持了可读性。你仍然可以使用为图标着色,但应仅用于传达含义(调用操作、下一步)——而不仅仅是视觉效果。
.tint()Toolbar Transitions & Morphing
工具栏过渡与变形
iOS 26 toolbars automatically morph between screens during NavigationStack push/pop transitions. The key insight: attach to individual views inside NavigationStack, not to NavigationStack itself.
.toolbar {}iOS 26工具栏在NavigationStack推送/弹出界面时会自动在屏幕间变形。核心要点:将附加到NavigationStack内的各个视图,而不是NavigationStack本身。
.toolbar {}DefaultToolbarItem
DefaultToolbarItem
iOS 26+. A toolbar item representing a system component. Use it to reposition system-provided items (like search) within your toolbar layout.
swift
struct DefaultToolbarItem {
init(kind: ToolbarDefaultItemKind, placement: ToolbarItemPlacement = .automatic)
}Key semantic: If the system has already placed a matching item in the toolbar, implicitly replaces the default-placed instance. This lets you move system items to different placements or reposition them relative to your own toolbar content.
kindDefaultToolbarItemiOS 26+。代表系统组件的工具栏项。用于在工具栏布局中重新定位系统提供的项(如搜索)。
swift
struct DefaultToolbarItem {
init(kind: ToolbarDefaultItemKind, placement: ToolbarItemPlacement = .automatic)
}核心语义:如果系统已在工具栏中放置了匹配的项,会隐式替换默认放置的实例。这让你可以将系统项移动到不同位置,或相对于自己的工具栏内容重新定位它们。
kindDefaultToolbarItemRepositioning search between toolbar items
在工具栏项之间重新定位搜索
swift
NavigationSplitView {
AllCalendarsView()
} detail: {
SelectedCalendarView()
.searchable(text: $query)
.toolbar {
ToolbarItem(placement: .bottomBar) {
CalendarPicker()
}
ToolbarItem(placement: .bottomBar) {
Invites()
}
DefaultToolbarItem(kind: .search, placement: .bottomBar)
ToolbarSpacer(placement: .bottomBar)
ToolbarItem(placement: .bottomBar) { NewEventButton() }
}
}swift
NavigationSplitView {
AllCalendarsView()
} detail: {
SelectedCalendarView()
.searchable(text: $query)
.toolbar {
ToolbarItem(placement: .bottomBar) {
CalendarPicker()
}
ToolbarItem(placement: .bottomBar) {
Invites()
}
DefaultToolbarItem(kind: .search, placement: .bottomBar)
ToolbarSpacer(placement: .bottomBar)
ToolbarItem(placement: .bottomBar) { NewEventButton() }
}
}Specifying search column in collapsed NavigationSplitView
在折叠的NavigationSplitView中指定搜索列
Place with kind in the column that should display search when the split view collapses to compact (iPhone):
DefaultToolbarItem.searchswift
NavigationSplitView {
SidebarView()
.toolbar {
DefaultToolbarItem(kind: .search, placement: .bottomBar)
}
} content: {
ContentView()
} detail: {
DetailView()
}
.searchable(text: $text)This only applies when is placed on the itself (not a child view).
.searchable()NavigationSplitView在拆分视图折叠为紧凑模式(iPhone)时应显示搜索的列中放置带有类型的:
.searchDefaultToolbarItemswift
NavigationSplitView {
SidebarView()
.toolbar {
DefaultToolbarItem(kind: .search, placement: .bottomBar)
}
} content: {
ContentView()
} detail: {
DetailView()
}
.searchable(text: $text)仅当放置在本身上(而非子视图)时才适用。
.searchable()NavigationSplitViewAvailability check for backward compatibility
向后兼容性的可用性检查
swift
.toolbar {
if #available(iOS 26.0, *) {
DefaultToolbarItem(kind: .search, placement: .bottomBar)
ToolbarSpacer(.flexible, placement: .bottomBar)
}
ToolbarItem(placement: .bottomBar) {
NewNoteButton()
}
}
.searchable(text: $searchText)swift
.toolbar {
if #available(iOS 26.0, *) {
DefaultToolbarItem(kind: .search, placement: .bottomBar)
ToolbarSpacer(.flexible, placement: .bottomBar)
}
ToolbarItem(placement: .bottomBar) {
NewNoteButton()
}
}
.searchable(text: $searchText)Basic Morphing Setup
基础变形设置
Each view declares its own toolbar. iOS 26 morphs between them during navigation:
swift
struct MailboxList: View {
var body: some View {
List(mailboxes) { mailbox in
NavigationLink(mailbox.name, value: mailbox)
}
.toolbar { // ← Attached to this view, not NavigationStack
ToolbarItem(placement: .bottomBar) {
Button("Filter", systemImage: "line.3.horizontal.decrease") { }
}
ToolbarSpacer(.flexible, placement: .bottomBar)
ToolbarItem(placement: .bottomBar) {
Button("New Message", systemImage: "square.and.pencil") { }
}
}
}
}
struct MessageList: View {
let mailbox: Mailbox
var body: some View {
List(mailbox.messages) { message in
MessageRow(message: message)
}
.toolbar { // ← Different toolbar — iOS 26 morphs between them
ToolbarSpacer(.flexible, placement: .bottomBar)
ToolbarItem(placement: .bottomBar) {
Button("New Message", systemImage: "square.and.pencil") { }
}
}
}
}#1 gotcha: If you attach to the NavigationStack itself, iOS 26 has nothing to morph between — the toolbar stays static across all pushes.
.toolbar {}每个视图都声明自己的工具栏。iOS 26在导航期间会在它们之间进行变形:
swift
struct MailboxList: View {
var body: some View {
List(mailboxes) { mailbox in
NavigationLink(mailbox.name, value: mailbox)
}
.toolbar { // ← 附加到该视图,而非NavigationStack
ToolbarItem(placement: .bottomBar) {
Button("Filter", systemImage: "line.3.horizontal.decrease") { }
}
ToolbarSpacer(.flexible, placement: .bottomBar)
ToolbarItem(placement: .bottomBar) {
Button("New Message", systemImage: "square.and.pencil") { }
}
}
}
}
struct MessageList: View {
let mailbox: Mailbox
var body: some View {
List(mailbox.messages) { message in
MessageRow(message: message)
}
.toolbar { // ← 不同的工具栏——iOS 26在它们之间变形
ToolbarSpacer(.flexible, placement: .bottomBar)
ToolbarItem(placement: .bottomBar) {
Button("New Message", systemImage: "square.and.pencil") { }
}
}
}
}常见误区#1:如果将附加到NavigationStack本身,iOS 26没有可变形的内容——工具栏在所有推送操作中保持静态。
.toolbar {}Stable Items with toolbar(id:)
and ToolbarItem(id:)
toolbar(id:)ToolbarItem(id:)使用toolbar(id:)
和ToolbarItem(id:)
实现稳定项
toolbar(id:)ToolbarItem(id:)Items with matching IDs across screens stay in place during morphing (no bounce). Unmatched items animate in/out:
swift
struct MailboxList: View {
var body: some View {
List(mailboxes) { mailbox in
NavigationLink(mailbox.name, value: mailbox)
}
.toolbar(id: "main") {
ToolbarItem(id: "filter", placement: .bottomBar) {
Button("Filter", systemImage: "line.3.horizontal.decrease") { }
}
ToolbarSpacer(.flexible, placement: .bottomBar)
ToolbarItem(id: "compose", placement: .bottomBar) {
Button("New Message", systemImage: "square.and.pencil") { }
}
}
}
}
struct MessageList: View {
let mailbox: Mailbox
var body: some View {
List(mailbox.messages) { message in
MessageRow(message: message)
}
.toolbar(id: "main") {
// "filter" absent — animates out during push
ToolbarSpacer(.flexible, placement: .bottomBar)
ToolbarItem(id: "compose", placement: .bottomBar) {
// Same ID as MailboxList — stays stable during morph
Button("New Message", systemImage: "square.and.pencil") { }
}
}
}
}不同屏幕上具有匹配ID的项在变形期间保持原位(无弹跳)。不匹配的项会动态进出:
swift
struct MailboxList: View {
var body: some View {
List(mailboxes) { mailbox in
NavigationLink(mailbox.name, value: mailbox)
}
.toolbar(id: "main") {
ToolbarItem(id: "filter", placement: .bottomBar) {
Button("Filter", systemImage: "line.3.horizontal.decrease") { }
}
ToolbarSpacer(.flexible, placement: .bottomBar)
ToolbarItem(id: "compose", placement: .bottomBar) {
Button("New Message", systemImage: "square.and.pencil") { }
}
}
}
}
struct MessageList: View {
let mailbox: Mailbox
var body: some View {
List(mailbox.messages) { message in
MessageRow(message: message)
}
.toolbar(id: "main") {
// "filter"不存在——推送时会动态消失
ToolbarSpacer(.flexible, placement: .bottomBar)
ToolbarItem(id: "compose", placement: .bottomBar) {
// 与MailboxList中的ID相同——变形期间保持稳定
Button("New Message", systemImage: "square.and.pencil") { }
}
}
}
}Complete Mail-Style Example
完整邮件风格示例
Combines DefaultToolbarItem, ToolbarSpacer, and stable compose button:
swift
struct MailApp: View {
var body: some View {
NavigationStack {
MailboxList()
.navigationDestination(for: Mailbox.self) { mailbox in
MessageList(mailbox: mailbox)
}
.navigationDestination(for: Message.self) { message in
MessageDetail(message: message)
}
}
}
}
// Each destination defines its own toolbar — NavigationStack morphs between them结合DefaultToolbarItem、ToolbarSpacer和稳定的撰写按钮:
swift
struct MailApp: View {
var body: some View {
NavigationStack {
MailboxList()
.navigationDestination(for: Mailbox.self) { mailbox in
MessageList(mailbox: mailbox)
}
.navigationDestination(for: Message.self) { message in
MessageDetail(message: message)
}
}
}
}
// 每个目标视图都定义自己的工具栏——NavigationStack在它们之间变形ToolbarSpacer(.flexible)
ToolbarSpacer(.flexible)
Pushes toolbar items apart (like Spacer in HStack). Complements for visual separation:
.fixedswift
.toolbar {
ToolbarItem(placement: .bottomBar) {
Button("Archive", systemImage: "archivebox") { }
}
ToolbarSpacer(.flexible, placement: .bottomBar) // Push apart
ToolbarItem(placement: .bottomBar) {
Button("Compose", systemImage: "square.and.pencil") { }
}
}将工具栏项推开(类似于HStack中的Spacer)。与配合实现视觉分隔:
.fixedswift
.toolbar {
ToolbarItem(placement: .bottomBar) {
Button("Archive", systemImage: "archivebox") { }
}
ToolbarSpacer(.flexible, placement: .bottomBar) // 推开项
ToolbarItem(placement: .bottomBar) {
Button("Compose", systemImage: "square.and.pencil") { }
}
}.navigationSubtitle()
.navigationSubtitle()
Add a secondary line below the navigation title:
swift
.navigationTitle("Inbox")
.navigationSubtitle("3 unread messages")在导航标题下方添加次要行:
swift
.navigationTitle("Inbox")
.navigationSubtitle("3 unread messages")User-Customizable Toolbars
用户可自定义工具栏
toolbar(id:content:)toolbar(id:content:)Setup
设置
swift
TextEditor(text: $text)
.toolbar(id: "editingtools") {
ToolbarItem(id: "bold", placement: .secondaryAction) {
Toggle(isOn: $bold) { Image(systemName: "bold") }
}
ToolbarItem(id: "italic", placement: .secondaryAction) {
Toggle(isOn: $italic) { Image(systemName: "italic") }
}
}Platform constraint: Only items support customization on iPadOS. Other placements follow normal rules and cannot be customized by the user.
.secondaryActionswift
TextEditor(text: $text)
.toolbar(id: "editingtools") {
ToolbarItem(id: "bold", placement: .secondaryAction) {
Toggle(isOn: $bold) { Image(systemName: "bold") }
}
ToolbarItem(id: "italic", placement: .secondaryAction) {
Toggle(isOn: $italic) { Image(systemName: "italic") }
}
}平台限制:在iPadOS上,只有项支持自定义。其他位置遵循常规规则,用户无法自定义。
.secondaryActionControlling visibility
控制可见性
swift
ToolbarItem(id: "advanced", placement: .secondaryAction, showsByDefault: false) {
// Hidden by default — user can add from customization editor
AdvancedFormattingControls()
}swift
ToolbarItem(id: "advanced", placement: .secondaryAction, showsByDefault: false) {
// 默认隐藏——用户可从自定义编辑器中添加
AdvancedFormattingControls()
}macOS toolbar customization menu
macOS工具栏自定义菜单
Add to enable the Customize Toolbar menu item:
ToolbarCommands()swift
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
.commands {
ToolbarCommands()
}
}
}Users can also Control-click the toolbar to access the customization editor.
添加以启用“自定义工具栏”菜单项:
ToolbarCommands()swift
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
.commands {
ToolbarCommands()
}
}
}用户还可以按住Control键点击工具栏以访问自定义编辑器。
Related types
相关类型
- — Protocol for content that supports customization
CustomizableToolbarContent - — Control whether items can be added/removed
ToolbarCustomizationBehavior - — Options for the customization experience
ToolbarCustomizationOptions
- ——支持自定义的内容协议
CustomizableToolbarContent - ——控制项是否可添加/移除
ToolbarCustomizationBehavior - ——自定义体验的选项
ToolbarCustomizationOptions
ToolbarSpacer in customizable toolbars
可自定义工具栏中的ToolbarSpacer
Spacers are customizable items too — users can add, remove, and rearrange them from the customization panel. If a customizable toolbar supports a spacer of a given type, users can also add multiple copies:
swift
ContentView()
.toolbar(id: "main-toolbar") {
ToolbarItem(id: "tag") { TagButton() }
ToolbarItem(id: "share") { ShareButton() }
ToolbarSpacer(.fixed)
ToolbarItem(id: "more") { MoreButton() }
}间隔器也是可自定义项——用户可从自定义面板添加、移除和重新排列它们。如果可自定义工具栏支持特定类型的间隔器,用户还可以添加多个副本:
swift
ContentView()
.toolbar(id: "main-toolbar") {
ToolbarItem(id: "tag") { TagButton() }
ToolbarItem(id: "share") { ShareButton() }
ToolbarSpacer(.fixed)
ToolbarItem(id: "more") { MoreButton() }
}Scroll Edge Effects
滚动边缘效果
Automatic blur on scroll edges
滚动边缘自动模糊
swift
ScrollView {
// When content scrolls under toolbar/navigation bar,
// blur effect automatically ensures bar content remains legible
ForEach(trips) { trip in
TripRow(trip: trip)
}
}
// No code required - automatic scroll edge blurswift
ScrollView {
// 当内容在工具栏/导航栏下滚动时,
// 模糊效果自动确保栏内容保持可读
ForEach(trips) { trip in
TripRow(trip: trip)
}
}
// 无需代码——自动应用滚动边缘模糊Bottom-Aligned Search
底部对齐搜索
Foundational search APIs For , , suggestions, scopes, tokens, and programmatic search control, see . This section covers iOS 26 refinements only.
.searchableisSearchingaxiom-swiftui-search-ref基础搜索API 如需了解、、建议、范围、标记和程序化搜索控制,请参阅。本节仅涵盖iOS 26的改进。
.searchableisSearchingaxiom-swiftui-search-refiPhone ergonomics
iPhone人体工程学
swift
NavigationSplitView {
List { }
.searchable(text: $searchText)
}
// Placement on NavigationSplitView automatically:
// - Bottom-aligned on iPhone (more ergonomic)
// - Top trailing corner on iPadTo restore pre-iOS 26 sidebar-embedded search on iPad, specify :
placement: .sidebarswift
NavigationSplitView {
List { }
.searchable(text: $searchText, placement: .sidebar)
// Search field embedded in sidebar instead of floating glass container
}swift
NavigationSplitView {
List { }
.searchable(text: $searchText)
}
// 放置在NavigationSplitView上会自动:
// - 在iPhone上底部对齐(更符合人体工程学)
// - 在iPad上位于右上角要恢复iPad上iOS 26之前的侧边栏嵌入搜索,请指定:
placement: .sidebarswift
NavigationSplitView {
List { }
.searchable(text: $searchText, placement: .sidebar)
// 搜索字段嵌入侧边栏,而非浮动玻璃容器
}System Auto-Minimization
系统自动最小化
Depending on device size, number of toolbar buttons, and other factors, the system may choose to minimize the search field into a toolbar button automatically. When tapped, a full-width search field appears above the keyboard. Use to explicitly opt in to this behavior when search isn't the main part of your experience.
searchToolbarBehavior(.minimize)根据设备尺寸、工具栏按钮数量和其他因素,系统可能会自动选择将搜索字段最小化为工具栏按钮。点击时,会在键盘上方显示全宽搜索字段。当搜索不是体验的主要部分时,使用显式启用此行为。
searchToolbarBehavior(.minimize)Search Tab Role
搜索标签页角色
Searching in multi-tab apps is often done as a dedicated page. Set a search role on one of your tabs and place on the TabView:
.searchableswift
TabView {
BrowseView()
.tabItem { Label("Browse", systemImage: "square.grid.2x2") }
Tab(role: .search) {
SearchView()
}
}
.searchable(text: $searchText)When someone selects the search tab, the search field takes the place of the tab bar. People can interact with browsing suggestions or tap the search field to bring up the keyboard.
On iPad and Mac, when someone selects the search tab, the search field appears centered above the app's browsing suggestions — a different layout than iPhone's tab-bar replacement.
See swiftui-nav-ref skill Section 5.5 (iOS 26 Tab Features) for full patterns.
Tab(role: .search)在多标签页应用中,搜索通常作为专用页面完成。在其中一个标签页上设置搜索角色,并将放置在TabView上:
.searchableswift
TabView {
BrowseView()
.tabItem { Label("Browse", systemImage: "square.grid.2x2") }
Tab(role: .search) {
SearchView()
}
}
.searchable(text: $searchText)当用户选择搜索标签页时,搜索字段会取代标签栏。用户可以与浏览建议交互,或点击搜索字段调出键盘。
在iPad和Mac上,当用户选择搜索标签页时,搜索字段会居中显示在应用浏览建议上方——这与iPhone的标签栏替换布局不同。
有关完整的模式,请参阅swiftui-nav-ref技能第5.5节(iOS 26标签页特性)。
Tab(role: .search)Glass Effect for Custom Views
自定义视图的玻璃效果
swift
struct ToTopButton: View {
var body: some View {
Button("To Top", systemImage: "chevron.up") {
scrollToTop()
}
.padding()
.glassEffect() // Reflects surrounding content
}
func scrollToTop() { }
}SwiftUI automatically uses a vibrant text color that adapts to maintain legibility against colorful backgrounds. Tints also use vibrant color that adapts to the content behind them.
On iOS, add the modifier to the glass effect for custom controls or containers with interactive elements. Glass reacts to user interaction by scaling, bouncing, and shimmering.
.interactiveswift
struct ToTopButton: View {
var body: some View {
Button("To Top", systemImage: "chevron.up") {
scrollToTop()
}
.padding()
.glassEffect() // 反射周围内容
}
func scrollToTop() { }
}SwiftUI自动使用鲜艳的文本颜色,可适应多彩背景以保持可读性。着色也会使用可适应背后内容的鲜艳颜色。
在iOS上,为自定义控件或带有交互元素的容器的玻璃效果添加修饰符。玻璃会通过缩放、弹跳和闪烁响应用户交互。
.interactiveGlassEffectContainer
GlassEffectContainer
Combine multiple glass elements with . This is essential for visual correctness — glass samples content from an area larger than itself, but glass cannot sample other glass. Nearby glass elements in different containers produce inconsistent behavior:
GlassEffectContainerswift
GlassEffectContainer {
HStack(spacing: 12) {
ForEach(badges) { badge in
BadgeView(badge: badge)
.glassEffect()
}
}
}使用组合多个玻璃元素。这对视觉正确性至关重要——玻璃会从比自身更大的区域采样内容,但玻璃无法采样其他玻璃。不同容器中的邻近玻璃元素会产生不一致的行为:
GlassEffectContainerswift
GlassEffectContainer {
HStack(spacing: 12) {
ForEach(badges) { badge in
BadgeView(badge: badge)
.glassEffect()
}
}
}glassEffectID for Morphing Transitions
用于变形过渡的glassEffectID
Use with a namespace to create fluid morphing transitions between glass elements:
glassEffectIDswift
@Namespace private var badgeNamespace
var body: some View {
if isExpanded {
// Expanded badge stack
GlassEffectContainer {
VStack {
ForEach(badges) { badge in
BadgeView(badge: badge)
.glassEffect()
.glassEffectID(badge.id, in: badgeNamespace)
}
}
}
} else {
// Collapsed into toolbar button
Button("Badges", systemImage: "star.fill") {
isExpanded.toggle()
}
.glassEffect()
.glassEffectID("collapsed", in: badgeNamespace)
}
}
// Tapping the button expands badges with fluid glass morphing;
// tapping again re-absorbs them into the button使用和命名空间在玻璃元素之间创建流畅的变形过渡:
glassEffectIDswift
@Namespace private var badgeNamespace
var body: some View {
if isExpanded {
// 展开的徽章堆叠
GlassEffectContainer {
VStack {
ForEach(badges) { badge in
BadgeView(badge: badge)
.glassEffect()
.glassEffectID(badge.id, in: badgeNamespace)
}
}
}
} else {
// 折叠为工具栏按钮
Button("Badges", systemImage: "star.fill") {
isExpanded.toggle()
}
.glassEffect()
.glassEffectID("collapsed", in: badgeNamespace)
}
}
// 点击按钮会使徽章以流畅的玻璃变形效果展开;再次点击会将它们重新吸收到按钮中Sheets with Liquid Glass
带Liquid Glass的Sheet
Remove presentationBackground
移除presentationBackground
Partial height sheets are inset by default with a Liquid Glass background. At smaller heights, bottom edges pull in, nesting in the curved edges of the display. When transitioning to full height, the glass gradually becomes opaque and anchors to the screen edge.
If you've used to apply a custom background to sheets, consider removing it and let the new material shine:
presentationBackgroundswift
// ❌ Custom background interferes with Liquid Glass sheet material
.sheet(isPresented: $showDetail) {
DetailView()
.presentationBackground(.thinMaterial) // Remove this
}
// ✅ System applies Liquid Glass automatically
.sheet(isPresented: $showDetail) {
DetailView()
}部分高度的Sheet默认使用Liquid Glass背景并向内缩进。在较小高度时,底部边缘向内收缩,嵌套在显示屏的弧形边缘中。过渡到全高度时,玻璃会逐渐变为不透明并锚定到屏幕边缘。
如果你曾使用为Sheet应用自定义背景,请考虑移除它,让新材质发挥作用:
presentationBackgroundswift
// ❌ 自定义背景会干扰Liquid Glass Sheet材质
.sheet(isPresented: $showDetail) {
DetailView()
.presentationBackground(.thinMaterial) // 移除此行
}
// ✅ 系统自动应用Liquid Glass
.sheet(isPresented: $showDetail) {
DetailView()
}Sheet Morphing from Buttons
从按钮变形的Sheet
Sheets can morph directly out of the buttons that present them. Use to connect the source view to the sheet content:
navigationZoomTransitionswift
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
Button("Details", systemImage: "info.circle") {
showDetails = true
}
.matchedTransitionSource(id: "details", in: namespace)
}
}
.sheet(isPresented: $showDetails) {
DetailsSheet()
.navigationTransition(.zoom(sourceID: "details", in: namespace))
}Menus, alerts, and popovers also flow smoothly out of Liquid Glass controls. Dialogs automatically morph out of the buttons that present them — no additional code needed.
Sheet可以直接从呈现它们的按钮变形出来。使用将源视图连接到Sheet内容:
navigationZoomTransitionswift
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
Button("Details", systemImage: "info.circle") {
showDetails = true
}
.matchedTransitionSource(id: "details", in: namespace)
}
}
.sheet(isPresented: $showDetails) {
DetailsSheet()
.navigationTransition(.zoom(sourceID: "details", in: namespace))
}菜单、警报和弹出框也会从Liquid Glass控件流畅地弹出。对话框会自动从呈现它们的按钮变形出来——无需额外代码。
System Controls Updates
系统控件更新
Controls now have the new design automatically:
- Toggles
- Segmented pickers
- Sliders
Reference "Build a SwiftUI app with the new design" (WWDC 2025-323) for adoption best practices and advanced customizations.
控件现在会自动获得新设计:
- 开关(Toggles)
- 分段选择器(Segmented pickers)
- 滑块(Sliders)
参考 “使用新设计构建SwiftUI应用”(WWDC 2025-323)了解采用最佳实践和高级自定义。
Button Shape Changes
按钮形状变化
Bordered buttons now have a capsule shape by default, harmonious with the curved corners of the new design. On macOS, mini, small, and medium size controls retain a rounded-rectangle shape to preserve horizontal density.
swift
// Capsule is now the default bordered shape on iOS
Button("Action") { }
.buttonStyle(.bordered) // Capsule shape on iOS 26
// Override with buttonBorderShape if needed
Button("Action") { }
.buttonStyle(.bordered)
.buttonBorderShape(.roundedRectangle) // Force rounded rectangle带边框的按钮现在默认采用胶囊形状,与新设计的圆角和谐统一。在macOS上,迷你、小和中等尺寸的控件保留圆角矩形形状以保持水平密度。
swift
// 在iOS上,胶囊现在是默认的带边框形状
Button("Action") { }
.buttonStyle(.bordered) // iOS 26上为胶囊形状
// 如有需要,使用buttonBorderShape覆盖
Button("Action") { }
.buttonStyle(.bordered)
.buttonBorderShape(.roundedRectangle) // 强制使用圆角矩形Extra Large Buttons
超大按钮
For prominent actions, iOS 26 adds support for extra large sized buttons:
swift
Button("Get Started") { startOnboarding() }
.buttonStyle(.borderedProminent)
.controlSize(.extraLarge) // New extra-large size对于突出操作,iOS 26添加了对超大尺寸按钮的支持:
swift
Button("Get Started") { startOnboarding() }
.buttonStyle(.borderedProminent)
.controlSize(.extraLarge) // 新的超大尺寸Updated Control Heights
更新的控件高度
Control heights are updated for the new design. Most controls on macOS are slightly taller, providing more breathing room around labels and enhancing click targets.
For compatibility with existing high-density layouts (complex inspectors, popovers), apply to a single control or an entire container:
controlSizeswift
// Preserve density in complex inspectors
Form {
// controls here
}
.controlSize(.small) // Maintains pre-iOS 26 density为新设计更新了控件高度。macOS上的大多数控件略高,为标签提供了更多呼吸空间并增强了点击目标。
为了与现有的高密度布局(复杂检查器、弹出框)兼容,将应用于单个控件或整个容器:
controlSizeswift
// 在复杂检查器中保留密度
Form {
// 控件位于此处
}
.controlSize(.small) // 保持iOS 26之前的密度Menu Cross-Platform Consistency
跨平台菜单一致性
Menus across platforms have a new design and more consistent layout. Icons are consistently on the leading edge and are now used on macOS too. The same API using or standard control initializers now creates the same result on both platforms:
Labelswift
Menu("Actions") {
Button("Copy", systemImage: "doc.on.doc") { copy() }
Button("Paste", systemImage: "doc.on.clipboard") { paste() }
Button("Delete", systemImage: "trash", role: .destructive) { delete() }
}
// Same result on iOS and macOS — icons on leading edge各平台的菜单都有了新设计和更一致的布局。图标始终位于左侧,现在macOS也使用图标。使用或标准控件初始化器的相同API现在在两个平台上产生相同的结果:
Labelswift
Menu("Actions") {
Button("Copy", systemImage: "doc.on.doc") { copy() }
Button("Paste", systemImage: "doc.on.clipboard") { paste() }
Button("Delete", systemImage: "trash", role: .destructive) { delete() }
}
// iOS和macOS上的结果相同——图标位于左侧Concentric Rectangle Shape
同心矩形形状
Controls align their corners perfectly within their containers — even the device itself. This is corner concentricity. A button at the bottom of a sheet should share the same corner center with the sheet.
swift
// Automatic concentricity with container
Button("Confirm") { confirm() }
.clipShape(.rect(cornerRadius: 12, style: .containerConcentric))
// Shape automatically matches container across displays and window shapesUse case: Buttons positioned at the edges of sheets, popovers, or cards that need their corners to feel visually nested within the container.
控件的角落与其容器(甚至设备本身)完美对齐。这就是角落同心度。Sheet底部的按钮应与Sheet共享相同的角中心。
swift
// 与容器自动同心
Button("Confirm") { confirm() }
.clipShape(.rect(cornerRadius: 12, style: .containerConcentric))
// 形状会自动匹配所有显示器和窗口形状的容器用例:位于Sheet、弹出框或卡片边缘的按钮,其角落需要在视觉上嵌套在容器中。
Slider Enhancements
滑块增强
iOS 26 introduces major enhancements to : custom tick marks, constrained selection ranges, current value labels, and thumb visibility control.
SlideriOS 26为引入了重大增强:自定义刻度标记、约束选择范围、当前值标签和滑块thumb可见性控制。
SliderSlider Ticks API
滑块刻度API
Core Types
核心类型
| Type | Purpose |
|---|---|
| Individual tick at a specific value with optional label |
| Iterate over collection to create multiple ticks |
| Result builder for composing tick content |
| Internal type for multiple inline ticks |
| Protocol that all tick types conform to |
| 类型 | 用途 |
|---|---|
| 特定值的单个刻度,可选标签 |
| 遍历集合以创建多个刻度 |
| 用于组合刻度内容的结果构建器 |
| 多个内联刻度的内部类型 |
| 所有刻度类型都遵循的协议 |
Basic Ticks
基础刻度
Static tick marks
静态刻度标记
swift
struct SpeedSlider: View {
@State private var speed: Double = 0.5
var body: some View {
Slider(value: $speed) {
Text("Speed")
} ticks: {
SliderTick(0.2)
SliderTick(0.5)
SliderTick(0.8)
}
}
}swift
struct SpeedSlider: View {
@State private var speed: Double = 0.5
var body: some View {
Slider(value: $speed) {
Text("Speed")
} ticks: {
SliderTick(0.2)
SliderTick(0.5)
SliderTick(0.8)
}
}
}Labeled Ticks
带标签的刻度
Ticks with custom labels
带自定义标签的刻度
swift
Slider(value: $value, in: 0...10) {
Text("Rating")
} ticks: {
SliderTick(0) { Text("Min") }
SliderTick(5) { Text("Mid") }
SliderTick(10) { Text("Max") }
}swift
Slider(value: $value, in: 0...10) {
Text("Rating")
} ticks: {
SliderTick(0) { Text("Min") }
SliderTick(5) { Text("Mid") }
SliderTick(10) { Text("Max") }
}Dynamic Ticks with SliderTickContentForEach
使用SliderTickContentForEach的动态刻度
Iterate over values to create ticks
遍历值以创建刻度
swift
struct TemperatureSlider: View {
@State private var temp: Float = 70
var body: some View {
let stops: [Float] = stride(from: 60, through: 80, by: 5).map { Float($0) }
Slider(value: $temp, in: 60...80) {
Text("Temperature")
} ticks: {
SliderTickContentForEach(stops, id: \.self) { value in
SliderTick(value) {
Text("\(Int(value))°")
.font(.caption2)
}
}
}
}
}swift
struct TemperatureSlider: View {
@State private var temp: Float = 70
var body: some View {
let stops: [Float] = stride(from: 60, through: 80, by: 5).map { Float($0) }
Slider(value: $temp, in: 60...80) {
Text("Temperature")
} ticks: {
SliderTickContentForEach(stops, id: \.self) { value in
SliderTick(value) {
Text("\(Int(value))°")
.font(.caption2)
}
}
}
}
}Custom Data with Ticks (API Constraint)
带刻度的自定义数据(API约束)
Important requires to match the value type. You cannot iterate directly over custom structs.
SliderTickContentForEachData.ElementSliderTick<V>重要 要求与值类型匹配。你不能直接遍历自定义结构体。
SliderTickContentForEachData.ElementSliderTick<V>❌ This won't compile
❌ 无法编译
swift
struct Chapter {
let time: Double
let name: String
let id: UUID
}
// ERROR: Data.Element (Chapter) doesn't match SliderTick value type (Double)
SliderTickContentForEach(chapters, id: \.id) { chapter in
SliderTick(chapter.time) { Text(chapter.name) }
}swift
struct Chapter {
let time: Double
let name: String
let id: UUID
}
// 错误:Data.Element (Chapter)与SliderTick值类型(Double)不匹配
SliderTickContentForEach(chapters, id: \.id) { chapter in
SliderTick(chapter.time) { Text(chapter.name) }
}✅ Workaround: Extract values, look up labels
✅ 解决方法:提取值,查找标签
swift
struct ChapterSlider: View {
@Binding var currentTime: Double
let chapters: [Chapter]
let duration: Double
var body: some View {
Slider(value: $currentTime, in: 0...duration) {
Text("Time")
} ticks: {
// Iterate over Double values, not Chapter structs
SliderTickContentForEach(chapters.map(\.time), id: \.self) { time in
SliderTick(time) {
// Look up chapter name for label
if let chapter = chapters.first(where: { $0.time == time }) {
Text(chapter.name)
.font(.caption2)
}
}
}
}
}
}Why The API ties tick positions to slider values (). The type system enforces this so ticks align correctly with the slider's value range.
BinaryFloatingPointswift
struct ChapterSlider: View {
@Binding var currentTime: Double
let chapters: [Chapter]
let duration: Double
var body: some View {
Slider(value: $currentTime, in: 0...duration) {
Text("Time")
} ticks: {
// 遍历Double值,而非Chapter结构体
SliderTickContentForEach(chapters.map(\.time), id: \.self) { time in
SliderTick(time) {
// 查找标签对应的章节名称
if let chapter = chapters.first(where: { $0.time == time }) {
Text(chapter.name)
.font(.caption2)
}
}
}
}
}
}原因 API将刻度位置与滑块值()绑定。类型系统强制执行此操作,以便刻度与滑块的值范围正确对齐。
BinaryFloatingPointAdvanced Slider Initializers
高级滑块初始化器
Full-featured slider with ticks
带刻度的全功能滑块
swift
Slider(
value: $rating,
in: 0...100,
neutralValue: 50, // Starting point / center value
enabledBounds: 20...80, // Restrict selectable range
label: { Text("Rating") },
currentValueLabel: { Text("\(Int(rating))") },
minimumValueLabel: { Text("0") },
maximumValueLabel: { Text("100") },
ticks: {
SliderTick(20) { Text("Min") }
SliderTick(50) { Text("Neutral") }
SliderTick(80) { Text("Max") }
},
onEditingChanged: { editing in
print(editing ? "Started" : "Ended")
}
)swift
Slider(
value: $rating,
in: 0...100,
neutralValue: 50, // 起始点/中心值
enabledBounds: 20...80, // 限制可选范围
label: { Text("Rating") },
currentValueLabel: { Text("\(Int(rating))") },
minimumValueLabel: { Text("0") },
maximumValueLabel: { Text("100") },
ticks: {
SliderTick(20) { Text("Min") }
SliderTick(50) { Text("Neutral") }
SliderTick(80) { Text("Max") }
},
onEditingChanged: { editing in
print(editing ? "Started" : "Ended")
}
)Parameters
参数
| Parameter | Type | Purpose |
|---|---|---|
| | Current slider value |
| | Full value range (default: |
| | Increment between valid values |
| | Starting/center point |
| | Restrict which values are selectable |
| | Custom tick marks |
| | Shows current value |
| | Called when editing starts/ends |
| 参数 | 类型 | 用途 |
|---|---|---|
| | 当前滑块值 |
| | 完整值范围(默认: |
| | 有效值之间的增量 |
| | 起始/中心点 |
| | 限制可选值范围 |
| | 自定义刻度标记 |
| | 显示当前值 |
| | 编辑开始/结束时调用 |
Step-Based Ticks
基于步长的刻度
Automatic ticks at each step
每个步长自动生成刻度
swift
Slider(
value: $volume,
in: 0...10,
step: 2,
label: { Text("Volume") },
tick: { value in
// Called for each step value (0, 2, 4, 6, 8, 10)
SliderTick(value) {
Text("\(Int(value))")
}
}
)swift
Slider(
value: $volume,
in: 0...10,
step: 2,
label: { Text("Volume") },
tick: { value in
// 为每个步长值调用(0, 2, 4, 6, 8, 10)
SliderTick(value) {
Text("\(Int(value))")
}
}
)sliderThumbVisibility
sliderThumbVisibility
Hide slider thumb for minimal interfaces
在极简界面中隐藏滑块thumb
swift
struct MediaControlView: View {
@State private var progress: CGFloat = 0.5
var body: some View {
Slider(value: $progress)
.sliderThumbVisibility(.hidden)
.padding(.horizontal, 16)
}
}Visibility options
- — System default (usually visible)
.automatic - — Always show thumb
.visible - — Hide thumb
.hidden
Use cases
- Media player progress indicators
- Read-only value displays
- Minimal UI designs where slider acts as progress view
- Interactive sliders where visual focus should be on track, not thumb
Note On watchOS, the slider thumb is always visible regardless of this setting.
swift
struct MediaControlView: View {
@State private var progress: CGFloat = 0.5
var body: some View {
Slider(value: $progress)
.sliderThumbVisibility(.hidden)
.padding(.horizontal, 16)
}
}可见性选项
- ——系统默认(通常可见)
.automatic - ——始终显示thumb
.visible - ——隐藏thumb
.hidden
用例
- 媒体播放器进度指示器
- 只读值显示
- 极简UI设计,其中滑块用作进度视图
- 交互式滑块,视觉焦点应在轨道而非thumb上
注意 在watchOS上,无论此设置如何,滑块thumb始终可见。
Complete Media Player Example
完整媒体播放器示例
swift
struct MediaPlayerControls: View {
@State private var currentTime: Double = 0
let duration: Double = 300 // 5 minutes
let chapters: [Chapter] = [
Chapter(time: 0, name: "Intro", id: UUID()),
Chapter(time: 60, name: "Verse 1", id: UUID()),
Chapter(time: 120, name: "Chorus", id: UUID()),
Chapter(time: 180, name: "Verse 2", id: UUID()),
Chapter(time: 240, name: "Outro", id: UUID())
]
var body: some View {
VStack {
// Time display
HStack {
Text(formatTime(currentTime))
Spacer()
Text(formatTime(duration))
}
.font(.caption)
// Slider with chapter ticks
Slider(
value: $currentTime,
in: 0...duration,
label: { Text("Playback") },
currentValueLabel: {
if let chapter = currentChapter {
Text(chapter.name)
.font(.caption)
}
},
ticks: {
SliderTickContentForEach(chapters.map(\.time), id: \.self) { time in
SliderTick(time)
}
}
)
.sliderThumbVisibility(.hidden)
}
.padding()
}
var currentChapter: Chapter? {
chapters.last { $0.time <= currentTime }
}
func formatTime(_ seconds: Double) -> String {
let mins = Int(seconds) / 60
let secs = Int(seconds) % 60
return String(format: "%d:%02d", mins, secs)
}
}swift
struct MediaPlayerControls: View {
@State private var currentTime: Double = 0
let duration: Double = 300 // 5分钟
let chapters: [Chapter] = [
Chapter(time: 0, name: "Intro", id: UUID()),
Chapter(time: 60, name: "Verse 1", id: UUID()),
Chapter(time: 120, name: "Chorus", id: UUID()),
Chapter(time: 180, name: "Verse 2", id: UUID()),
Chapter(time: 240, name: "Outro", id: UUID())
]
var body: some View {
VStack {
// 时间显示
HStack {
Text(formatTime(currentTime))
Spacer()
Text(formatTime(duration))
}
.font(.caption)
// 带章节刻度的滑块
Slider(
value: $currentTime,
in: 0...duration,
label: { Text("Playback") },
currentValueLabel: {
if let chapter = currentChapter {
Text(chapter.name)
.font(.caption)
}
},
ticks: {
SliderTickContentForEach(chapters.map(\.time), id: \.self) { time in
SliderTick(time)
}
}
)
.sliderThumbVisibility(.hidden)
}
.padding()
}
var currentChapter: Chapter? {
chapters.last { $0.time <= currentTime }
}
func formatTime(_ seconds: Double) -> String {
let mins = Int(seconds) / 60
let secs = Int(seconds) % 60
return String(format: "%d:%02d", mins, secs)
}
}New View Modifiers
新视图修饰符
safeAreaBar
safeAreaBar
Sticky bars with progressive blur
带渐进模糊的粘性栏
swift
struct ContentView: View {
var body: some View {
NavigationStack {
List {
ForEach(1...20, id: \.self) { index in
Text("\(index). Item")
}
}
.safeAreaBar(edge: .bottom) {
Text("Bottom Action Bar")
.padding(.vertical, 15)
}
.scrollEdgeEffectStyle(.soft, for: .bottom)
// Alternative: .scrollEdgeEffectStyle(.hard, for: .bottom)
}
}
}Features
- Works like but with integrated blur
safeAreaInset - Progressive blur () or hard blur (
.soft) via.hardscrollEdgeEffectStyle - Automatically respects safe areas
- Bar remains fixed while content scrolls beneath
Use cases
- Action bars that remain visible while scrolling
- Fixed controls at screen edges
- Bottom toolbars with scroll blur
swift
struct ContentView: View {
var body: some View {
NavigationStack {
List {
ForEach(1...20, id: \.self) { index in
Text("\(index). Item")
}
}
.safeAreaBar(edge: .bottom) {
Text("Bottom Action Bar")
.padding(.vertical, 15)
}
.scrollEdgeEffectStyle(.soft, for: .bottom)
// 替代方案:.scrollEdgeEffectStyle(.hard, for: .bottom)
}
}
}特性
- 类似于,但集成了模糊效果
safeAreaInset - 通过提供渐进模糊(
scrollEdgeEffectStyle)或硬模糊(.soft).hard - 自动尊重安全区域
- 栏保持固定,内容在其下方滚动
用例
- 滚动时保持可见的操作栏
- 屏幕边缘的固定控件
- 带滚动模糊的底部工具栏
onOpenURL Enhancement
onOpenURL增强
Open links in in-app browser
在应用内浏览器中打开链接
swift
struct LinkView: View {
@Environment(\.openURL) var openURL
var body: some View {
let website = URL(string: "https://example.com")!
VStack {
// Old style - opens in Safari
Link(destination: website) {
Text("Open in Safari")
}
// New style - opens in-app (iOS 26+)
Button("Open In-App") {
openURL(website, prefersInApp: true)
}
.buttonStyle(.borderedProminent)
}
}
}Key difference
- Default opens in Safari app
Link - opens in SFSafariViewController-style in-app browser
openURL(url, prefersInApp: true) - Keeps users in your app
- Preserves navigation flow
swift
struct LinkView: View {
@Environment(\.openURL) var openURL
var body: some View {
let website = URL(string: "https://example.com")!
VStack {
// 旧样式——在Safari中打开
Link(destination: website) {
Text("Open in Safari")
}
// 新样式——在应用内打开(iOS 26+)
Button("Open In-App") {
openURL(website, prefersInApp: true)
}
.buttonStyle(.borderedProminent)
}
}
}核心区别
- 默认在Safari应用中打开
Link - 在SFSafariViewController样式的应用内浏览器中打开
openURL(url, prefersInApp: true) - 让用户留在你的应用中
- 保留导航流程
Button Roles (.close and .confirm)
按钮角色(.close和.confirm)
System-styled close and confirm buttons
系统样式的关闭和确认按钮
swift
struct ModalView: View {
@State private var showSheet = false
var body: some View {
Button("Show Sheet") {
showSheet.toggle()
}
.sheet(isPresented: $showSheet) {
NavigationStack {
VStack {}
.navigationTitle("Info")
.toolbar {
ToolbarSpacer(.flexible, placement: .topBarTrailing)
ToolbarItem(placement: .topBarTrailing) {
Button(role: .close) {
showSheet = false
}
}
}
}
.presentationDetents([.medium])
}
}
}Features
- renders as X icon with glass effect in toolbars
Button(role: .close) - provides system-styled confirmation button
Button(role: .confirm) - No custom label needed
- Consistent with system modals and sheets
Use cases
- Modal dismissal
- Sheet close buttons
- Confirmation dialogs
- Native-looking dismiss actions
swift
struct ModalView: View {
@State private var showSheet = false
var body: some View {
Button("Show Sheet") {
showSheet.toggle()
}
.sheet(isPresented: $showSheet) {
NavigationStack {
VStack {}
.navigationTitle("Info")
.toolbar {
ToolbarSpacer(.flexible, placement: .topBarTrailing)
ToolbarItem(placement: .topBarTrailing) {
Button(role: .close) {
showSheet = false
}
}
}
}
.presentationDetents([.medium])
}
}
}特性
- 在工具栏中渲染为带玻璃效果的X图标
Button(role: .close) - 提供系统样式的确认按钮
Button(role: .confirm) - 无需自定义标签
- 与系统模态框和Sheet一致
用例
- 模态框关闭
- Sheet关闭按钮
- 确认对话框
- 原生风格的关闭操作
GlassButtonStyle (iOS 26.1+)
GlassButtonStyle(iOS 26.1+)
Glass button variations
玻璃按钮变体
swift
struct GlassButtonExample: View {
var body: some View {
ZStack {
Image(.background)
.resizable()
.aspectRatio(contentMode: .fill)
.ignoresSafeArea()
VStack(spacing: 16) {
Button("Clear Glass") {}
.buttonStyle(GlassButtonStyle(.clear))
Button("Regular Glass") {}
.buttonStyle(GlassButtonStyle(.glass))
Button("Tinted Glass") {}
.buttonStyle(GlassButtonStyle(.tint))
.tint(.blue)
}
.fontWeight(.bold)
.foregroundStyle(.white)
.buttonSizing(.flexible)
.font(.title)
.padding()
}
}
}Variants
- — Transparent glass effect
.clear - — Standard glass appearance
.glass - — Colored glass (use with
.tintmodifier).tint()
Requires iOS 26.1+ (not available in initial iOS 26.0 release)
swift
struct GlassButtonExample: View {
var body: some View {
ZStack {
Image(.background)
.resizable()
.aspectRatio(contentMode: .fill)
.ignoresSafeArea()
VStack(spacing: 16) {
Button("Clear Glass") {}
.buttonStyle(GlassButtonStyle(.clear))
Button("Regular Glass") {}
.buttonStyle(GlassButtonStyle(.glass))
Button("Tinted Glass") {}
.buttonStyle(GlassButtonStyle(.tint))
.tint(.blue)
}
.fontWeight(.bold)
.foregroundStyle(.white)
.buttonSizing(.flexible)
.font(.title)
.padding()
}
}
}变体
- ——透明玻璃效果
.clear - ——标准玻璃外观
.glass - ——彩色玻璃(与
.tint修饰符一起使用).tint()
要求 iOS 26.1+(初始iOS 26.0版本中不可用)
buttonSizing
buttonSizing
Control button layout behavior
控制按钮布局行为
swift
struct ButtonLayoutExample: View {
var body: some View {
VStack(spacing: 16) {
Button("Fit Content") {}
.buttonSizing(.fit)
// Button shrinks to label size
Button("Stretch Full Width") {}
.buttonSizing(.stretch)
// Button expands to fill available space
Button("Flexible") {}
.buttonSizing(.flexible)
// Balanced between fit and stretch
}
.padding()
}
}Options
- — Button fits label size
.fit - — Button fills available width
.stretch - — Balanced sizing (context-dependent)
.flexible
Works with
- Plain text buttons
- Custom labels (icon + text, HStack/VStack)
- All button styles
swift
struct ButtonLayoutExample: View {
var body: some View {
VStack(spacing: 16) {
Button("Fit Content") {}
.buttonSizing(.fit)
// 按钮缩小到标签大小
Button("Stretch Full Width") {}
.buttonSizing(.stretch)
// 按钮扩展以填充可用空间
Button("Flexible") {}
.buttonSizing(.flexible)
// 在适应和拉伸之间取得平衡
}
.padding()
}
}选项
- ——按钮适应标签大小
.fit - ——按钮填充可用宽度
.stretch - ——平衡大小(取决于上下文)
.flexible
适用对象
- 纯文本按钮
- 自定义标签(图标+文本、HStack/VStack)
- 所有按钮样式
searchToolbarBehavior
searchToolbarBehavior
Foundational search APIs For the modifier, placement options, and search suggestions that builds upon, see .
.searchable.searchToolbarBehavioraxiom-swiftui-search-ref基础搜索API 如需了解修饰符、位置选项以及所基于的搜索建议,请参阅。
.searchablesearchToolbarBehavioraxiom-swiftui-search-refCompact search that expands on focus
获得焦点时展开的紧凑搜索
swift
struct SearchView: View {
@State private var searchText = ""
var body: some View {
NavigationStack {
List {
Text("User 1")
Text("User 2")
Text("User 3")
}
.navigationTitle("Search Users")
.searchable(text: $searchText)
.searchToolbarBehavior(.minimize)
.toolbar {
ToolbarSpacer(.flexible, placement: .bottomBar)
DefaultToolbarItem(kind: .search, placement: .bottomBar)
}
}
}
}SearchToolbarBehavior- — Search field compact (button-like) when unfocused, expands on tap
.minimize - — System default behavior (full search field)
.automatic
Behavior
- Similar to Tab Bar search pattern
- Saves toolbar space
- Cleaner UI when search not in use
Use cases
- List/content-heavy screens
- Crowded navigation bars
- Tab bar style search on regular screens
Backward-compatible wrapper for apps targeting iOS 18+26:
swift
extension View {
@ViewBuilder func minimizedSearch() -> some View {
if #available(iOS 26.0, *) {
self.searchToolbarBehavior(.minimize)
} else { self }
}
}
// Usage
.searchable(text: $searchText)
.minimizedSearch()swift
struct SearchView: View {
@State private var searchText = ""
var body: some View {
NavigationStack {
List {
Text("User 1")
Text("User 2")
Text("User 3")
}
.navigationTitle("Search Users")
.searchable(text: $searchText)
.searchToolbarBehavior(.minimize)
.toolbar {
ToolbarSpacer(.flexible, placement: .bottomBar)
DefaultToolbarItem(kind: .search, placement: .bottomBar)
}
}
}
}SearchToolbarBehavior- ——未获得焦点时搜索字段紧凑(类似按钮),点击时展开
.minimize - ——系统默认行为(全宽搜索字段)
.automatic
行为
- 类似于标签栏搜索模式
- 节省工具栏空间
- 搜索未使用时UI更简洁
用例
- 列表/内容密集的屏幕
- 拥挤的导航栏
- 常规屏幕上的标签栏样式搜索
针对iOS 18+26的向后兼容包装器:
swift
extension View {
@ViewBuilder func minimizedSearch() -> some View {
if #available(iOS 26.0, *) {
self.searchToolbarBehavior(.minimize)
} else { self }
}
}
// 使用方式
.searchable(text: $searchText)
.minimizedSearch()searchPresentationToolbarBehavior (iOS 17.1+)
searchPresentationToolbarBehavior(iOS 17.1+)
Prevent title hiding during search
搜索期间防止标题隐藏
swift
.searchable(text: $searchText)
.searchPresentationToolbarBehavior(.avoidHidingContent)Behavior
- By default, navigation title hides when search becomes active
- keeps title visible during search
.avoidHidingContent - Maintains context while searching
Note This modifier was introduced in iOS 17.1, not iOS 26, but complements the new modifier.
searchToolbarBehaviorswift
.searchable(text: $searchText)
.searchPresentationToolbarBehavior(.avoidHidingContent)行为
- 默认情况下,搜索激活时导航标题会隐藏
- 在搜索期间保持标题可见
.avoidHidingContent - 搜索时保持上下文
注意 此修饰符在iOS 17.1中引入,而非iOS 26,但与新的修饰符互补。
searchToolbarBehavioriPad Enhancements
iPad增强
Menu Bar
菜单栏
Access common actions via swipe-down menu
通过向下滑动菜单访问常用操作
swift
.commands {
TextEditingCommands() // Same API as macOS menu bar
CommandGroup(after: .newItem) {
Button("Add Note") {
addNote()
}
.keyboardShortcut("n", modifiers: [.command, .shift])
}
}
// Creates menu bar on iPad when people swipe downswift
.commands {
TextEditingCommands() // 与macOS菜单栏相同的API
CommandGroup(after: .newItem) {
Button("Add Note") {
addNote()
}
.keyboardShortcut("n", modifiers: [.command, .shift])
}
}
// 用户向下滑动时在iPad上创建菜单栏Resizable Windows
可调整大小的窗口
Fluid resizing on iPad
iPad上的流畅调整大小
swift
// MIGRATION REQUIRED:
// Remove deprecated property list key in iPadOS 26:
// UIRequiresFullscreen (entire key deprecated, all values)
// For split view navigation, system automatically shows/hides columns
// based on available space during resize
NavigationSplitView {
Sidebar()
} detail: {
Detail()
}
// Adapts to resizing automaticallyReference "Elevate the design of your iPad app" (WWDC 2025)
swift
// 需要迁移:
// 在iPadOS 26中移除已弃用的属性列表键:
// UIRequiresFullscreen(整个键已弃用,所有值)
// 对于拆分视图导航,系统会根据调整大小时的可用空间自动显示/隐藏列
NavigationSplitView {
Sidebar()
} detail: {
Detail()
}
// 自动适应调整大小参考 “提升iPad应用的设计”(WWDC 2025)
macOS Window Enhancements
macOS窗口增强
Synchronized Window Resize Animations
同步窗口调整大小动画
swift
.windowResizeAnchor(.topLeading) // Tailor where animation originates
// SwiftUI now synchronizes animation between content view size changes
// and window resizing - great for preserving continuity when switching tabsswift
.windowResizeAnchor(.topLeading) // 定制动画起始位置
// SwiftUI现在同步内容视图大小变化与窗口调整大小之间的动画——非常适合切换标签页时保持连续性Performance Improvements
性能改进
List Performance (macOS Focus)
列表性能(macOS重点)
Massive gains for large lists
大型列表的大幅提升
- 6x faster loading for lists of 100,000+ items on macOS
- 16x faster updates for large lists
- Even bigger gains for larger lists
- Improvements benefit all platforms (iOS, iPadOS, watchOS)
swift
List(trips) { trip in // 100k+ items
TripRow(trip: trip)
}
// Loads 6x faster, updates 16x faster on macOS (iOS 26+)- macOS上100,000+项的列表加载速度快6倍
- 大型列表更新速度快16倍
- 列表越大,提升越明显
- 改进惠及所有平台(iOS、iPadOS、watchOS)
swift
List(trips) { trip in // 100k+项
TripRow(trip: trip)
}
// macOS上加载速度快6倍,更新速度快16倍(iOS 26+)Scrolling Performance
滚动性能
Reduced dropped frames
减少掉帧
SwiftUI has improved scheduling of user interface updates on iOS and macOS. This improves responsiveness and lets SwiftUI do even more work to prepare for upcoming frames. All in all, it reduces the chance of your app dropping a frame while scrolling quickly at high frame rates.
SwiftUI改进了iOS和macOS上用户界面更新的调度。这提高了响应能力,并让SwiftUI能够做更多工作来准备即将到来的帧。总而言之,这降低了应用在高帧率下快速滚动时掉帧的可能性。
Nested ScrollViews with Lazy Stacks
带Lazy Stacks的嵌套ScrollViews
Photo carousels and multi-axis scrolling
照片轮播和多轴滚动
swift
ScrollView(.horizontal) {
LazyHStack {
ForEach(photoSets) { photoSet in
ScrollView(.vertical) {
LazyVStack {
ForEach(photoSet.photos) { photo in
PhotoView(photo: photo)
}
}
}
}
}
}
// Nested scrollviews now properly delay loading with lazy stacks
// Great for building photo carouselsswift
ScrollView(.horizontal) {
LazyHStack {
ForEach(photoSets) { photoSet in
ScrollView(.vertical) {
LazyVStack {
ForEach(photoSet.photos) { photo in
PhotoView(photo: photo)
}
}
}
}
}
}
// 嵌套ScrollView现在通过lazy stacks正确延迟加载
// 非常适合构建照片轮播SwiftUI Performance Instrument
SwiftUI性能工具
New profiling tool in Xcode
Xcode中的新分析工具
Available lanes:
- Long view body updates — Identify expensive body computations
- Platform view updates — Track UIKit/AppKit bridging performance
- Other performance problem areas
Reference "Optimize SwiftUI performance with instruments" (WWDC 2025)
Cross-reference SwiftUI Performance — Master the SwiftUI Instrument
可用通道:
- 长视图body更新——识别昂贵的body计算
- 平台视图更新——跟踪UIKit/AppKit桥接性能
- 其他性能问题区域
参考 “使用工具优化SwiftUI性能”(WWDC 2025)
交叉参考 SwiftUI性能——掌握SwiftUI工具
Swift Concurrency Integration
Swift并发集成
Compile-Time Data Race Safety
编译时数据竞争安全
swift
@Observable
class TripStore {
var trips: [Trip] = []
func loadTrips() async {
trips = await TripService.fetchTrips()
// Swift 6 verifies data race safety at compile time
}
}Benefits Find bugs in concurrent code before they affect your app
swift
@Observable
class TripStore {
var trips: [Trip] = []
func loadTrips() async {
trips = await TripService.fetchTrips()
// Swift 6在编译时验证数据竞争安全
}
}优势 在并发代码影响应用之前发现bug
References
参考
- "Embracing Swift concurrency" (WWDC 2025)
- "Explore concurrency in SwiftUI" (WWDC 2025)
Cross-reference Swift Concurrency — Swift 6 strict concurrency patterns
@Animatable Macro
@Animatable宏
Overview
概述
Simplifies custom animations by automatically synthesizing property.
animatableData通过自动合成属性简化自定义动画。
animatableDataBefore (@Animatable macro)
之前(@Animatable宏出现前)
swift
struct HikingRouteShape: Shape {
var startPoint: CGPoint
var endPoint: CGPoint
var elevation: Double
var drawingDirection: Bool // Don't want to animate this
// Tedious manual animatableData declaration
var animatableData: AnimatablePair<CGPoint.AnimatableData,
AnimatablePair<Double, CGPoint.AnimatableData>> {
get {
AnimatablePair(startPoint.animatableData,
AnimatablePair(elevation, endPoint.animatableData))
}
set {
startPoint.animatableData = newValue.first
elevation = newValue.second.first
endPoint.animatableData = newValue.second.second
}
}
}swift
struct HikingRouteShape: Shape {
var startPoint: CGPoint
var endPoint: CGPoint
var elevation: Double
var drawingDirection: Bool // 不想为此设置动画
// 繁琐的手动animatableData声明
var animatableData: AnimatablePair<CGPoint.AnimatableData,
AnimatablePair<Double, CGPoint.AnimatableData>> {
get {
AnimatablePair(startPoint.animatableData,
AnimatablePair(elevation, endPoint.animatableData))
}
set {
startPoint.animatableData = newValue.first
elevation = newValue.second.first
endPoint.animatableData = newValue.second.second
}
}
}After (@Animatable macro)
之后(使用@Animatable宏)
swift
@Animatable
struct HikingRouteShape: Shape {
var startPoint: CGPoint
var endPoint: CGPoint
var elevation: Double
@AnimatableIgnored
var drawingDirection: Bool // Excluded from animation
// animatableData automatically synthesized!
}swift
@Animatable
struct HikingRouteShape: Shape {
var startPoint: CGPoint
var endPoint: CGPoint
var elevation: Double
@AnimatableIgnored
var drawingDirection: Bool // 排除在动画之外
// animatableData自动合成!
}Key benefits
核心优势
- Delete manual property
animatableData - Use for properties to exclude
@AnimatableIgnored - SwiftUI automatically synthesizes animation data
Cross-reference SwiftUI Animation (swiftui-animation-ref skill) — Comprehensive animation guide covering VectorArithmetic, Animatable protocol, @Animatable macro, animation types, Transaction system, and performance optimization
- 删除手动属性
animatableData - 使用标记要排除的属性
@AnimatableIgnored - SwiftUI自动合成动画数据
交叉参考 SwiftUI动画(swiftui-animation-ref技能)——涵盖VectorArithmetic、Animatable协议、@Animatable宏、动画类型、Transaction系统和性能优化的综合动画指南
3D Spatial Layout (visionOS)
3D空间布局(visionOS)
Alignment3D
Alignment3D
Depth-based layout
基于深度的布局
swift
struct SunPositionView: View {
@State private var timeOfDay: Double = 12.0
var body: some View {
HikingRouteView()
.overlay(alignment: sunAlignment) {
SunView()
.spatialOverlay(alignment: sunAlignment)
}
}
var sunAlignment: Alignment3D {
// Align sun in 3D space based on time of day
Alignment3D(
horizontal: .center,
vertical: .top,
depth: .back
)
}
}swift
struct SunPositionView: View {
@State private var timeOfDay: Double = 12.0
var body: some View {
HikingRouteView()
.overlay(alignment: sunAlignment) {
SunView()
.spatialOverlay(alignment: sunAlignment)
}
}
var sunAlignment: Alignment3D {
// 根据一天中的时间在3D空间中对齐太阳
Alignment3D(
horizontal: .center,
vertical: .top,
depth: .back
)
}
}Manipulable Modifier
Manipulable修饰符
Interactive 3D objects
交互式3D对象
swift
Model3D(named: "WaterBottle")
.manipulable() // People can pick up and move the objectswift
Model3D(named: "WaterBottle")
.manipulable() // 用户可以拿起并移动对象Surface Snapping APIs
表面吸附API
swift
@Environment(\.surfaceSnappingInfo) var snappingInfo: SurfaceSnappingInfo
var body: some View {
VStackLayout().depthAlignment(.center) {
Model3D(named: "waterBottle")
.manipulable()
Pedestal()
.opacity(snappingInfo.classification == .table ? 1.0 : 0.0)
}
}swift
@Environment(\.surfaceSnappingInfo) var snappingInfo: SurfaceSnappingInfo
var body: some View {
VStackLayout().depthAlignment(.center) {
Model3D(named: "waterBottle")
.manipulable()
Pedestal()
.opacity(snappingInfo.classification == .table ? 1.0 : 0.0)
}
}References
参考
- "Meet SwiftUI spatial layout" (WWDC 2025)
- "Set the scene with SwiftUI in visionOS" (WWDC 2025)
- "What's new in visionOS" (WWDC 2025)
- “认识SwiftUI空间布局”(WWDC 2025)
- “在visionOS中使用SwiftUI设置场景”(WWDC 2025)
- “visionOS新特性”(WWDC 2025)
Scene Bridging
场景桥接
Overview
概述
Scene bridging allows your UIKit and AppKit lifecycle apps to interoperate with SwiftUI scenes. Apps can use it to open SwiftUI-only scene types or use SwiftUI-exclusive features right from UIKit or AppKit code.
场景桥接允许你的UIKit和AppKit生命周期应用与SwiftUI场景互操作。应用可以使用它来打开仅支持SwiftUI的场景类型,或直接从UIKit或AppKit代码中使用SwiftUI独有的功能。
Supported Scene Types
支持的场景类型
From UIKit/AppKit apps, you can now use
从UIKit/AppKit应用中,你现在可以使用
- (macOS)
MenuBarExtra - (visionOS)
ImmersiveSpace - (macOS → Vision Pro)
RemoteImmersiveSpace - (iOS 26)
AssistiveAccess
- (macOS)
MenuBarExtra - (visionOS)
ImmersiveSpace - (macOS → Vision Pro)
RemoteImmersiveSpace - (iOS 26)
AssistiveAccess
Scene Modifiers
场景修饰符
Works with scene modifiers like:
.windowStyle().immersiveEnvironmentBehavior()
与场景修饰符一起使用,如:
.windowStyle().immersiveEnvironmentBehavior()
RemoteImmersiveSpace
RemoteImmersiveSpace
Mac app renders stereo content on Vision Pro
Mac应用在Vision Pro上渲染立体内容
swift
// In your macOS app
@main
struct MyMacApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
RemoteImmersiveSpace(id: "stereoView") {
// Render stereo content on Apple Vision Pro
// Uses CompositorServices
}
}
}swift
// 在你的macOS应用中
@main
struct MyMacApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
RemoteImmersiveSpace(id: "stereoView") {
// 在Apple Vision Pro上渲染立体内容
// 使用CompositorServices
}
}
}Features
特性
- Mac app renders stereo content on Vision Pro
- Hover effects and input events supported
- Uses CompositorServices and Metal
Reference "What's new in Metal rendering for immersive apps" (WWDC 2025)
- Mac应用在Vision Pro上渲染立体内容
- 支持悬停效果和输入事件
- 使用CompositorServices和Metal
参考 “沉浸式应用的Metal渲染新特性”(WWDC 2025)
AssistiveAccess Scene
AssistiveAccess场景
Special mode for users with cognitive disabilities
为认知障碍用户设计的特殊模式
swift
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
AssistiveAccessScene {
SimplifiedUI() // UI shown when iPhone is in AssistiveAccess mode
}
}
}Reference "Customize your app for Assistive Access" (WWDC 2025)
swift
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
AssistiveAccessScene {
SimplifiedUI() // iPhone处于AssistiveAccess模式时显示的UI
}
}
}参考 “为辅助访问自定义你的应用”(WWDC 2025)
AppKit Integration Enhancements
AppKit集成增强
SwiftUI Sheets in AppKit
AppKit中的SwiftUI Sheets
swift
// Show SwiftUI view in AppKit sheet
let hostingController = NSHostingController(rootView: SwiftUISettingsView())
presentAsSheet(hostingController)
// Great for incremental SwiftUI adoptionswift
// 在AppKit Sheet中显示SwiftUI视图
let hostingController = NSHostingController(rootView: SwiftUISettingsView())
presentAsSheet(hostingController)
// 非常适合逐步采用SwiftUINSGestureRecognizerRepresentable
NSGestureRecognizerRepresentable
swift
// Bridge AppKit gestures to SwiftUI
struct AppKitPanGesture: NSGestureRecognizerRepresentable {
func makeNSGestureRecognizer(context: Context) -> NSPanGestureRecognizer {
NSPanGestureRecognizer()
}
func updateNSGestureRecognizer(_ recognizer: NSPanGestureRecognizer, context: Context) {
// Update configuration
}
}swift
// 将AppKit手势桥接到SwiftUI
struct AppKitPanGesture: NSGestureRecognizerRepresentable {
func makeNSGestureRecognizer(context: Context) -> NSPanGestureRecognizer {
NSPanGestureRecognizer()
}
func updateNSGestureRecognizer(_ recognizer: NSPanGestureRecognizer, context: Context) {
// 更新配置
}
}NSHostingView in Interface Builder
Interface Builder中的NSHostingView
NSHostingView can now be used directly in Interface Builder for gradual SwiftUI adoption.
现在可以直接在Interface Builder中使用NSHostingView,逐步采用SwiftUI。
RealityKit Integration
RealityKit集成
Observable Entities
可观察实体
swift
@Observable
class RealityEntity {
var position: SIMD3<Float>
var rotation: simd_quatf
}
struct MyView: View {
@State private var entity = RealityEntity()
var body: some View {
// SwiftUI views automatically observe changes
Text("Position: \(entity.position.x)")
}
}swift
@Observable
class RealityEntity {
var position: SIMD3<Float>
var rotation: simd_quatf
}
struct MyView: View {
@State private var entity = RealityEntity()
var body: some View {
// SwiftUI视图自动观察变化
Text("Position: \(entity.position.x)")
}
}PresentationComponent
PresentationComponent
Present SwiftUI popovers, alerts, and sheets directly from RealityKit entities.
swift
// Present SwiftUI popovers from RealityKit entities
let popover = Entity()
mapEntity.addChild(popover)
popover.components[PresentationComponent.self] = PresentationComponent(
isPresented: $popoverPresented,
configuration: .popover(arrowEdge: .bottom),
content: DetailsView()
)直接从RealityKit实体呈现SwiftUI弹出框、警报和Sheet。
swift
// 从RealityKit实体呈现SwiftUI弹出框
let popover = Entity()
mapEntity.addChild(popover)
popover.components[PresentationComponent.self] = PresentationComponent(
isPresented: $popoverPresented,
configuration: .popover(arrowEdge: .bottom),
content: DetailsView()
)Additional Improvements
其他改进
- — add SwiftUI views to entities
ViewAttachmentComponent - — entity touch and gesture responsiveness
GestureComponent - Enhanced coordinate conversion API
- Synchronizing animations, binding to components
- New sizing behaviors for RealityView
Reference "Better Together: SwiftUI & RealityKit" (WWDC 2025)
- ——将SwiftUI视图添加到实体
ViewAttachmentComponent - ——实体触摸和手势响应
GestureComponent - 增强的坐标转换API
- 同步动画、绑定到组件
- RealityView的新尺寸行为
参考 “更好的组合:SwiftUI & RealityKit”(WWDC 2025)
WebView & WebPage
WebView & WebPage
Overview
概述
WebKit now provides full SwiftUI APIs for embedding web content, eliminating the need to drop down to UIKit.
WebKit现在提供用于嵌入网页内容的完整SwiftUI API,无需降级到UIKit。
WebView
WebView
Display web content
显示网页内容
swift
import WebKit
struct ArticleView: View {
let articleURL: URL
var body: some View {
WebView(url: articleURL)
}
}swift
import WebKit
struct ArticleView: View {
let articleURL: URL
var body: some View {
WebView(url: articleURL)
}
}WebPage (Observable Model)
WebPage(可观察模型)
Rich interaction with web content
与网页内容的丰富交互
swift
import WebKit
struct InAppBrowser: View {
@State private var page = WebPage()
var body: some View {
VStack {
Text(page.title ?? "Loading...")
WebView(page)
.ignoresSafeArea()
.onAppear {
page.load(URLRequest(url: articleURL))
}
HStack {
Button("Back") { page.goBack() }
.disabled(!page.canGoBack)
Button("Forward") { page.goForward() }
.disabled(!page.canGoForward)
}
}
}
}swift
import WebKit
struct InAppBrowser: View {
@State private var page = WebPage()
var body: some View {
VStack {
Text(page.title ?? "Loading...")
WebView(page)
.ignoresSafeArea()
.onAppear {
page.load(URLRequest(url: articleURL))
}
HStack {
Button("Back") { page.goBack() }
.disabled(!page.canGoBack)
Button("Forward") { page.goForward() }
.disabled(!page.canGoForward)
}
}
}
}WebPage features
WebPage特性
- Programmatic navigation (,
goBack())goForward() - Access page properties (,
title,url,canGoBack)canGoForward - Observable — SwiftUI views update automatically
- 程序化导航(、
goBack())goForward() - 访问页面属性(、
title、url、canGoBack)canGoForward - 可观察——SwiftUI视图自动更新
Advanced WebKit Features
高级WebKit特性
- Custom user agents
- JavaScript execution
- Custom URL schemes
- And more
Reference "Meet WebKit for SwiftUI" (WWDC 2025)
- 自定义用户代理
- JavaScript执行
- 自定义URL方案
- 等等
参考 “认识SwiftUI的WebKit”(WWDC 2025)
TextEditor with AttributedString
带AttributedString的TextEditor
Overview
概述
SwiftUI's new support for rich text editing is great for experiences like commenting on photos. TextView now supports AttributedString!
Note The WWDC transcript uses "TextView" as editorial language. The actual SwiftUI API is which now supports binding for rich text editing.
TextEditorAttributedStringSwiftUI对富文本编辑的新支持非常适合照片评论等体验。TextView现在支持AttributedString!
注意 WWDC文稿中使用“TextView”作为编辑术语。实际SwiftUI API是,现在支持绑定以进行富文本编辑。
TextEditorAttributedStringRich Text Editing
富文本编辑
swift
struct CommentView: View {
@State private var comment = AttributedString("Enter your comment")
var body: some View {
TextEditor(text: $comment)
// Built-in text formatting controls included
// Users can apply bold, italic, underline, etc.
}
}swift
struct CommentView: View {
@State private var comment = AttributedString("Enter your comment")
var body: some View {
TextEditor(text: $comment)
// 包含内置文本格式控件
// 用户可以应用粗体、斜体、下划线等
}
}Features
特性
- Built-in text formatting controls (bold, italic, underline, colors, etc.)
- Binding to preserves formatting
AttributedString - Automatic toolbar with formatting options
- 内置文本格式控件(粗体、斜体、下划线、颜色等)
- 绑定到以保留格式
AttributedString - 带格式选项的自动工具栏
Advanced AttributedString Features
高级AttributedString特性
Customization options
自定义选项
- Paragraph styles
- Attribute transformations
- Constrain which attributes users can apply
Reference "Cook up a rich text experience in SwiftUI with AttributedString" (WWDC 2025)
Cross-reference App Intents Integration (app-intents-ref skill) — AttributedString for Apple Intelligence Use Model action
- 段落样式
- 属性转换
- 限制用户可以应用的属性
参考 “使用AttributedString在SwiftUI中打造富文本体验”(WWDC 2025)
交叉参考 App Intents集成(app-intents-ref技能)——Apple Intelligence使用模型操作的AttributedString
Drag and Drop Enhancements
拖放增强
Multiple Item Dragging
多项目拖放
Drag multiple items based on selection
根据选择拖放多个项目
swift
struct PhotoGrid: View {
@State private var selectedPhotos: [Photo.ID] = []
var body: some View {
ScrollView {
LazyVGrid(columns: gridColumns) {
ForEach(model.photos) { photo in
view(photo: photo)
.draggable(containerItemID: photo.id)
}
}
}
.dragContainer(for: Photo.self, selection: selectedPhotos) { draggedIDs in
photos(ids: draggedIDs)
}
}
}Key APIs:
- marks each item as part of a drag container (namespace defaults to
.draggable(containerItemID:containerNamespace:))nil - provides the typed items lazily when a drop occurs
.dragContainer(for:selection:)
swift
struct PhotoGrid: View {
@State private var selectedPhotos: [Photo.ID] = []
var body: some View {
ScrollView {
LazyVGrid(columns: gridColumns) {
ForEach(model.photos) { photo in
view(photo: photo)
.draggable(containerItemID: photo.id)
}
}
}
.dragContainer(for: Photo.self, selection: selectedPhotos) { draggedIDs in
photos(ids: draggedIDs)
}
}
}核心API:
- 标记每个项为拖放容器的一部分(命名空间默认为
.draggable(containerItemID:containerNamespace:))nil - 在拖放发生时延迟提供类型化项
.dragContainer(for:selection:)
DragConfiguration
DragConfiguration
Customize supported operations
自定义支持的操作
swift
.dragConfiguration(DragConfiguration(allowMove: false, allowDelete: true))swift
.dragConfiguration(DragConfiguration(allowMove: false, allowDelete: true))Observing Drag Events
观察拖放事件
swift
.onDragSessionUpdated { session in
let ids = session.draggedItemIDs(for: Photo.ID.self)
if session.phase == .ended(.delete) {
trash(ids)
deletePhotos(ids)
}
}swift
.onDragSessionUpdated { session in
let ids = session.draggedItemIDs(for: Photo.ID.self)
if session.phase == .ended(.delete) {
trash(ids)
deletePhotos(ids)
}
}Drag Preview Formations
拖放预览布局
swift
.dragPreviewsFormation(.stack) // Items stack nicely on top of one another
// Other formations:
// - .default
// - .grid
// - .stackswift
.dragPreviewsFormation(.stack) // 项整齐堆叠在彼此之上
// 其他布局:
// - .default
// - .grid
// - .stackComplete Example
完整示例
swift
struct PhotoLibrary: View {
@State private var selectedPhotos: [Photo.ID] = []
var body: some View {
ScrollView {
LazyVGrid(columns: gridColumns) {
ForEach(model.photos) { photo in
view(photo: photo)
.draggable(containerItemID: photo.id)
}
}
}
.dragContainer(for: Photo.self, selection: selectedPhotos) { draggedIDs in
photos(ids: draggedIDs)
}
.dragConfiguration(DragConfiguration(allowMove: false, allowDelete: true))
.dragPreviewsFormation(.stack)
.onDragSessionUpdated { session in
let ids = session.draggedItemIDs(for: Photo.ID.self)
if session.phase == .ended(.delete) {
trash(ids)
deletePhotos(ids)
}
}
}
}swift
struct PhotoLibrary: View {
@State private var selectedPhotos: [Photo.ID] = []
var body: some View {
ScrollView {
LazyVGrid(columns: gridColumns) {
ForEach(model.photos) { photo in
view(photo: photo)
.draggable(containerItemID: photo.id)
}
}
}
.dragContainer(for: Photo.self, selection: selectedPhotos) { draggedIDs in
photos(ids: draggedIDs)
}
.dragConfiguration(DragConfiguration(allowMove: false, allowDelete: true))
.dragPreviewsFormation(.stack)
.onDragSessionUpdated { session in
let ids = session.draggedItemIDs(for: Photo.ID.self)
if session.phase == .ended(.delete) {
trash(ids)
deletePhotos(ids)
}
}
}
}3D Charts
3D图表
Overview
概述
Swift Charts now supports three-dimensional plotting with .
Chart3DSwift Charts现在支持使用进行三维绘图。
Chart3DBasic Usage
基础用法
From WWDC 256:21:35
来自WWDC 256:21:35
swift
import Charts
struct HikePlotView: View {
var body: some View {
Chart3D {
SurfacePlot(x: "x", y: "y", z: "z") { x, y in
sin(x) * cos(y)
}
.foregroundStyle(Gradient(colors: [.orange, .pink]))
}
.chartXScale(domain: -3...3)
.chartYScale(domain: -3...3)
.chartZScale(domain: -3...3)
}
}swift
import Charts
struct HikePlotView: View {
var body: some View {
Chart3D {
SurfacePlot(x: "x", y: "y", z: "z") { x, y in
sin(x) * cos(y)
}
.foregroundStyle(Gradient(colors: [.orange, .pink]))
}
.chartXScale(domain: -3...3)
.chartYScale(domain: -3...3)
.chartZScale(domain: -3...3)
}
}Features
特性
- container
Chart3D - for continuous surface rendering from a function
SurfacePlot - Z-axis specific modifiers (,
.chartZScale(), etc.).chartZAxis() - All existing chart marks with 3D variants (e.g., )
LineMark3D
Reference "Bring Swift Charts to the third dimension" (WWDC 2025)
- 容器
Chart3D - 用于从函数渲染连续表面
SurfacePlot - Z轴特定修饰符(、
.chartZScale()等).chartZAxis() - 所有现有图表标记的3D变体(如)
LineMark3D
参考 “将Swift Charts带入三维空间”(WWDC 2025)
Widgets & Controls
小组件与控件
Controls on watchOS and macOS
watchOS和macOS上的控件
watchOS 26
watchOS 26
swift
struct FavoriteLocationControl: ControlWidget {
var body: some ControlWidgetConfiguration {
StaticControlConfiguration(kind: "FavoriteLocation") {
ControlWidgetButton(action: MarkFavoriteIntent()) {
Label("Mark Favorite", systemImage: "star")
}
}
}
}
// Access from watch face or Shortcutsswift
struct FavoriteLocationControl: ControlWidget {
var body: some ControlWidgetConfiguration {
StaticControlConfiguration(kind: "FavoriteLocation") {
ControlWidgetButton(action: MarkFavoriteIntent()) {
Label("Mark Favorite", systemImage: "star")
}
}
}
}
// 从表盘或快捷指令访问macOS
macOS
Controls now appear in Control Center on Mac.
控件现在出现在Mac的控制中心。
Widgets on visionOS
visionOS上的小组件
Level of detail customization
细节级别自定义
swift
struct CountdownWidget: Widget {
var body: some WidgetConfiguration {
StaticConfiguration(kind: "Countdown") { entry in
CountdownView(entry: entry)
}
}
}
struct PhotoCountdownView: View {
@Environment(\.levelOfDetail) var levelOfDetail: LevelOfDetail
var body: some View {
switch levelOfDetail {
case .default:
RecentPhotosView() // Full detail when close
case .simplified:
CountdownView() // Simplified when further away
default:
CountdownView()
}
}
}swift
struct CountdownWidget: Widget {
var body: some WidgetConfiguration {
StaticConfiguration(kind: "Countdown") { entry in
CountdownView(entry: entry)
}
}
}
struct PhotoCountdownView: View {
@Environment(\.levelOfDetail) var levelOfDetail: LevelOfDetail
var body: some View {
switch levelOfDetail {
case .default:
RecentPhotosView() // 靠近时显示完整细节
case .simplified:
CountdownView() // 远离时显示简化版本
default:
CountdownView()
}
}
}Widgets on CarPlay
CarPlay上的小组件
Live Activities on CarPlay
CarPlay上的Live Activities
Live Activities now appear on CarPlay displays for glanceable information while driving.
Live Activities现在出现在CarPlay显示屏上,供驾驶时快速查看信息。
Additional Widget Features
其他小组件特性
- Push-based updating API
- New relevance APIs for watchOS
Reference "What's new in widgets" (WWDC 2025)
- 推送式更新API
- watchOS的新相关性API
参考 “小组件新特性”(WWDC 2025)
Migration Checklist
迁移清单
Deprecated APIs
已弃用API
❌ Remove in iPadOS 26
❌ 在iPadOS 26中移除
xml
<key>UIRequiresFullscreen</key>
<!-- Entire property list key is deprecated (all values) -->Apps must support resizable windows on iPad.
xml
<key>UIRequiresFullscreen</key>
<!-- 整个属性列表键已弃用(所有值) -->应用必须支持iPad上的可调整大小窗口。
Automatic Adoptions (Recompile Only)
自动采用(仅需重新编译)
✅ Liquid Glass design for navigation, tab bars, toolbars
✅ Bottom-aligned search on iPhone
✅ List performance improvements (6x loading, 16x updating)
✅ Scrolling performance improvements
✅ System controls (toggles, pickers, sliders) new appearance
✅ Bordered buttons default to capsule shape
✅ Updated control heights (slightly taller on macOS)
✅ Monochrome icon rendering in toolbars
✅ Menus: icons on leading edge, consistent across iOS and macOS
✅ Sheets morph out of dialogs automatically
✅ Scroll edge blur/fade under system toolbars
✅ 导航、标签栏、工具栏的Liquid Glass设计
✅ iPhone上的底部对齐搜索
✅ 列表性能改进(加载快6倍,更新快16倍)
✅ 滚动性能改进
✅ 系统控件(开关、选择器、滑块)新外观
✅ 带边框按钮默认采用胶囊形状
✅ 更新的控件高度(macOS上略高)
✅ 工具栏中的单色图标渲染
✅ 菜单:图标位于左侧,iOS和macOS上一致
✅ Sheets自动从对话框变形
✅ 系统工具栏下的滚动边缘模糊/淡出
Audit Items (Remove Old Customizations)
审计项(移除旧自定义)
⚠️ Remove from sheets (let Liquid Glass material shine)
⚠️ Remove extra backgrounds/darkening effects behind toolbar areas
⚠️ Remove hard-coded control heights (use automatic sizing)
⚠️ Update section headers to title-style capitalization (no longer auto-uppercased)
presentationBackground⚠️ 从Sheets中移除(让Liquid Glass材质发挥作用)
⚠️ 移除工具栏区域后的额外背景/变暗效果
⚠️ 移除硬编码控件高度(使用自动尺寸)
⚠️ 更新节标题为标题式大写(不再自动大写)
presentationBackgroundManual Adoptions (Code Changes)
手动采用(代码更改)
🔧 Toolbar spacers ()
🔧 Tinted prominent buttons in toolbars
🔧 Glass effect for custom views ()
🔧 for morphing transitions between glass elements
🔧 for multiple nearby glass elements
🔧 to remove toolbar item from group background
🔧 Sheet morphing from buttons ()
🔧 Search tab role ()
🔧 Compact search toolbar ()
🔧 Extra large buttons ()
🔧 Concentric rectangle shape ()
🔧 iPad menu bar ()
🔧 Window resize anchor ()
🔧 @Animatable macro for custom shapes/modifiers
🔧 WebView for web content
🔧 TextEditor with AttributedString binding
🔧 Enhanced drag and drop with
🔧 Slider ticks (, )
🔧 Slider thumb visibility ()
🔧 Safe area bars with blur ( + )
🔧 In-app URL opening ()
🔧 Close and confirm button roles ()
🔧 Glass button styles ( — iOS 26.1+)
🔧 Button sizing control ()
🔧 Toolbar morphing transitions (per-view inside NavigationStack)
🔧 DefaultToolbarItem for system components in toolbars
🔧 Stable toolbar items ( with matched IDs across screens)
🔧 User-customizable toolbars ( with )
🔧 Tab bar minimization ()
🔧 Tab view bottom accessory ( — iOS 26.1+)
.fixed.glassEffect()glassEffectIDGlassEffectContainersharedBackgroundVisibility(.hidden)navigationZoomTransitionTab(role: .search).searchToolbarBehavior(.minimize).controlSize(.extraLarge).containerConcentric.commands.windowResizeAnchor().dragContainerSliderTickSliderTickContentForEach.sliderThumbVisibility().safeAreaBar().scrollEdgeEffectStyle()openURL(url, prefersInApp: true)Button(role: .close)GlassButtonStyle.buttonSizing().toolbar {}toolbar(id:)toolbar(id:)CustomizableToolbarContent.tabBarMinimizeBehavior(.onScrollDown).tabViewBottomAccessory(isEnabled:content:)🔧 工具栏间隔器()
🔧 工具栏中的着色突出按钮
🔧 自定义视图的玻璃效果()
🔧 用于玻璃元素之间变形过渡的
🔧 用于多个邻近玻璃元素的
🔧 从组背景中移除工具栏项
🔧 从按钮变形的Sheet()
🔧 搜索标签页角色()
🔧 紧凑搜索工具栏()
🔧 超大按钮()
🔧 同心矩形形状()
🔧 iPad菜单栏()
🔧 窗口调整大小锚点()
🔧 用于自定义形状/修饰符的@Animatable宏
🔧 用于网页内容的WebView
🔧 带AttributedString绑定的TextEditor
🔧 带的增强拖放
🔧 滑块刻度(、)
🔧 滑块thumb可见性()
🔧 带模糊的安全区域栏( + )
🔧 应用内URL打开()
🔧 关闭和确认按钮角色()
🔧 玻璃按钮样式( — iOS 26.1+)
🔧 按钮尺寸控制()
🔧 工具栏变形过渡(NavigationStack内每个视图的)
🔧 用于工具栏中系统组件的DefaultToolbarItem
🔧 稳定工具栏项(跨屏幕使用匹配ID的)
🔧 用户可自定义工具栏(带的)
🔧 标签栏最小化()
🔧 标签视图底部附件( — iOS 26.1+)
.fixed.glassEffect()glassEffectIDGlassEffectContainersharedBackgroundVisibility(.hidden)navigationZoomTransitionTab(role: .search).searchToolbarBehavior(.minimize).controlSize(.extraLarge).containerConcentric.commands.windowResizeAnchor().dragContainerSliderTickSliderTickContentForEach.sliderThumbVisibility().safeAreaBar().scrollEdgeEffectStyle()openURL(url, prefersInApp: true)Button(role: .close)GlassButtonStyle.buttonSizing().toolbar {}toolbar(id:)CustomizableToolbarContenttoolbar(id:).tabBarMinimizeBehavior(.onScrollDown).tabViewBottomAccessory(isEnabled:content:)Best Practices
最佳实践
Performance
性能
DO
建议
- Profile with new SwiftUI performance instrument
- Use lazy stacks in nested ScrollViews
- Trust automatic list performance improvements
- 使用新SwiftUI性能工具进行分析
- 在嵌套ScrollViews中使用lazy stacks
- 信任自动列表性能改进
DON'T
不建议
- Over-optimize - let framework improvements help first
- Ignore long view body updates in profiler
- 过度优化——先让框架改进发挥作用
- 忽略分析器中的长视图body更新
Liquid Glass Design
Liquid Glass设计
DO
建议
- Recompile and test automatic appearance
- Use toolbar spacers for logical grouping
- Apply glass effect to custom views that benefit from reflections
- Attach toolbars to individual views for automatic morphing transitions
- Use with matching IDs for items that should stay stable across screens
toolbar(id:)
- 重新编译并测试自动外观
- 使用工具栏间隔器进行逻辑分组
- 将玻璃效果应用于可从反射中受益的自定义视图
- 将工具栏附加到各个视图以实现自动变形过渡
- 对跨屏幕应保持稳定的项使用带匹配ID的
toolbar(id:)
DON'T
不建议
- Fight the automatic design - embrace consistency
- Over-tint toolbars (use for prominence only, not decoration)
- Attach the toolbar to NavigationStack and expect morphing (attach to views inside it)
- Apply to sheets (remove it for Liquid Glass material)
presentationBackground - Place nearby glass elements in separate containers (use )
GlassEffectContainer - Add extra backgrounds or darkening effects behind system toolbar areas
- 抗拒自动设计——拥抱一致性
- 过度着色工具栏(仅用于突出显示,而非装饰)
- 将工具栏附加到NavigationStack并期望变形(附加到其中的视图)
- 为Sheets应用(移除它以使用Liquid Glass材质)
presentationBackground - 将邻近玻璃元素放在单独容器中(使用)
GlassEffectContainer - 在系统工具栏区域后添加额外背景或变暗效果
Layout & Spacing
布局与间距
DO
建议
- Use for edge-to-edge content (iOS 17+)
.safeAreaPadding() - Combine with Liquid Glass materials extending edge-to-edge
.safeAreaPadding() - Use for internal spacing between views
.padding()
- 对边缘到边缘内容使用(iOS 17+)
.safeAreaPadding() - 将与边缘到边缘的Liquid Glass材质结合使用
.safeAreaPadding() - 对视图之间的内部间距使用
.padding()
DON'T
不建议
- Use when content extends to screen edges (ignores notch/home indicator)
.padding() - Manually calculate safe area insets with GeometryReader on iOS 17+ (use instead)
.safeAreaPadding()
Reference: See skill for complete vs guide, or for Liquid Glass-specific safe area patterns.
axiom-swiftui-layout-ref.safeAreaPadding().padding()axiom-liquid-glass-ref- 内容延伸到屏幕边缘时使用(忽略刘海/主屏幕指示器)
.padding() - 在iOS 17+上使用GeometryReader手动计算安全区域插入(改用)
.safeAreaPadding()
参考 有关与的完整指南,请参阅技能,或有关Liquid Glass特定安全区域模式的。
.safeAreaPadding().padding()axiom-swiftui-layout-refaxiom-liquid-glass-refRich Text
富文本
DO
建议
- Use binding for
AttributedStringTextEditor - Constrain attributes if needed for your use case
- Consider localization with rich text
- 对使用
TextEditor绑定AttributedString - 根据用例限制属性
- 考虑富文本的本地化
DON'T
不建议
- Use plain and lose formatting
String - Allow all attributes without considering UX
- 使用普通并丢失格式
String - 允许所有属性而不考虑用户体验
Spatial Layout (visionOS)
空间布局(visionOS)
DO
建议
- Use for depth-based layouts
Alignment3D - Enable for objects users should interact with
.manipulable() - Check surface snapping state for context-aware UI
- 对基于深度的布局使用
Alignment3D - 为用户应交互的对象启用
.manipulable() - 检查表面吸附状态以实现上下文感知UI
DON'T
不建议
- Use 2D alignment APIs for 3D layouts
- Make all objects manipulable (only what makes sense)
- 对3D布局使用2D对齐API
- 使所有对象可操作(仅对有意义的对象使用)
Troubleshooting
故障排除
Issue: Liquid Glass appearance not showing
问题:Liquid Glass外观未显示
Symptom App still has old design after updating to iOS 26 SDK
症状 更新到iOS 26 SDK后应用仍具有旧设计
Solution
解决方案
- Clean build folder (Shift-Cmd-K)
- Rebuild with Xcode 16+ targeting iOS 26 SDK
- Check deployment target is iOS 26+
- 清理构建文件夹(Shift-Cmd-K)
- 使用Xcode 16+重新构建,目标为iOS 26 SDK
- 检查部署目标为iOS 26+
Issue: Bottom-aligned search not appearing on iPhone
问题:iPhone上未显示底部对齐搜索
Symptom Search remains at top on iPhone
症状 搜索在iPhone上仍位于顶部
Solution
解决方案
swift
// ✅ CORRECT: searchable on NavigationSplitView
NavigationSplitView {
List { }
.searchable(text: $query)
}
// ❌ WRONG: searchable on List directly in non-navigation context
List { }
.searchable(text: $query)swift
// ✅ 正确:searchable在NavigationSplitView上
NavigationSplitView {
List { }
.searchable(text: $query)
}
// ❌ 错误:searchable直接在非导航上下文的List上
List { }
.searchable(text: $query)Issue: @Animatable macro not synthesizing animatableData
问题:@Animatable宏未合成animatableData
Symptom Compile error "Type does not conform to Animatable"
症状 编译错误“类型不符合Animatable”
Solution
解决方案
swift
// Ensure all properties are either:
// 1. VectorArithmetic conforming types (Double, CGFloat, CGPoint, etc.)
// 2. Marked with @AnimatableIgnored
@Animatable
struct MyShape: Shape {
var radius: Double // ✅ VectorArithmetic
var position: CGPoint // ✅ VectorArithmetic
@AnimatableIgnored
var fillColor: Color // ✅ Ignored (Color is not VectorArithmetic)
}swift
// 确保所有属性要么:
// 1. 符合VectorArithmetic类型(Double、CGFloat、CGPoint等)
// 2. 标记为@AnimatableIgnored
@Animatable
struct MyShape: Shape {
var radius: Double // ✅ 符合VectorArithmetic
var position: CGPoint // ✅ 符合VectorArithmetic
@AnimatableIgnored
var fillColor: Color // ✅ 被忽略(Color不符合VectorArithmetic)
}Issue: AttributedString formatting lost in TextEditor
问题:TextEditor中AttributedString格式丢失
Symptom Rich text formatting disappears
症状 富文本格式消失
Solution
解决方案
swift
// ✅ CORRECT: Binding to AttributedString
@State private var text = AttributedString("Hello")
TextEditor(text: $text)
// ❌ WRONG: Binding to String
@State private var text = "Hello"
TextEditor(text: $text) // Plain String loses formattingswift
// ✅ 正确:绑定到AttributedString
@State private var text = AttributedString("Hello")
TextEditor(text: $text)
// ❌ 错误:绑定到String
@State private var text = "Hello"
TextEditor(text: $text) // 普通String会丢失格式Issue: Drag and drop delete not working
问题:拖放删除不起作用
Symptom Dragging to Dock trash doesn't delete items
症状 拖到程序坞废纸篓不会删除项目
Solution
解决方案
swift
// Must enable delete in drag configuration
.dragConfiguration(DragConfiguration(allowMove: false, allowDelete: true))
// And observe the delete event
.onDragSessionUpdated { session in
if session.phase == .ended(.delete) {
deleteItems()
}
}swift
// 必须在拖放配置中启用删除
.dragConfiguration(DragConfiguration(allowMove: false, allowDelete: true))
// 并观察删除事件
.onDragSessionUpdated { session in
if session.phase == .ended(.delete) {
deleteItems()
}
}Issue: SliderTickContentForEach won't compile with custom structs
问题:SliderTickContentForEach与自定义结构体一起使用时无法编译
Symptom Compile error when iterating over custom types like
[Chapter]swift
// ERROR: Cannot convert value of type 'Chapter' to expected argument type
SliderTickContentForEach(chapters, id: \.id) { chapter in
SliderTick(chapter.time) { ... }
}症状 遍历自定义类型如时编译错误
[Chapter]swift
// 错误:无法将类型'Chapter'的值转换为预期参数类型
SliderTickContentForEach(chapters, id: \.id) { chapter in
SliderTick(chapter.time) { ... }
}Solution
解决方案
SliderTickContentForEachData.ElementSliderTick<V>swift
// ✅ CORRECT: Iterate over Double values
SliderTickContentForEach(chapters.map(\.time), id: \.self) { time in
SliderTick(time) {
if let chapter = chapters.first(where: { $0.time == time }) {
Text(chapter.name)
}
}
}Why The API enforces type safety between tick positions and slider values. This is an API design constraint, not a bug.
SliderTickContentForEachData.ElementSliderTick<V>swift
// ✅ 正确:遍历Double值
SliderTickContentForEach(chapters.map(\.time), id: \.self) { time in
SliderTick(time) {
if let chapter = chapters.first(where: { $0.time == time }) {
Text(chapter.name)
}
}
}原因 API强制刻度位置与滑块值之间的类型安全。这是API设计约束,而非bug。
Issue: Toolbar morphing not working
问题:工具栏变形不起作用
Symptom Toolbar stays static during NavigationStack push/pop — no morphing animation
症状 工具栏在NavigationStack推送/弹出期间保持静态——无变形动画
Solution
解决方案
swift
// ❌ WRONG: Toolbar on NavigationStack — nothing to morph between
NavigationStack {
ContentView()
}
.toolbar {
ToolbarItem { Button("Action") { } }
}
// ✅ CORRECT: Toolbar on each destination view — iOS 26 morphs between them
NavigationStack {
ListView()
.toolbar {
ToolbarItem { Button("Filter") { } }
}
.navigationDestination(for: Item.self) { item in
DetailView(item: item)
.toolbar {
ToolbarItem { Button("Edit") { } }
}
}
}Move from the NavigationStack to each view inside it. iOS 26 morphs between per-view toolbars during navigation transitions.
.toolbar {}swift
// ❌ 错误:工具栏在NavigationStack上——无可变形内容
NavigationStack {
ContentView()
}
.toolbar {
ToolbarItem { Button("Action") { } }
}
// ✅ 正确:工具栏在每个目标视图上——iOS 26在它们之间变形
NavigationStack {
ListView()
.toolbar {
ToolbarItem { Button("Filter") { } }
}
.navigationDestination(for: Item.self) { item in
DetailView(item: item)
.toolbar {
ToolbarItem { Button("Edit") { } }
}
}
}将从NavigationStack移至其中的每个视图。iOS 26在导航过渡期间在每个视图的工具栏之间变形。
.toolbar {}Resources
资源
WWDC: 2025-256, 2025-278 (What's new in widgets), 2025-287 (Meet WebKit for SwiftUI), 2025-310 (Optimize SwiftUI performance with instruments), 2025-323 (Build a SwiftUI app with the new design), 2025-325 (Bring Swift Charts to the third dimension), 2025-341 (Cook up a rich text experience in SwiftUI with AttributedString)
Docs: /swiftui, /swiftui/defaulttoolbaritem, /swiftui/toolbarspacer, /swiftui/searchtoolbarbehavior, /swiftui/view/toolbar(id:content:), /swiftui/view/tabbarminimizebehavior(_:), /swiftui/view/tabviewbottomaccessory(isenabled:content:), /swiftui/slider, /swiftui/slidertick, /swiftui/slidertickcontentforeach, /webkit, /foundation/attributedstring, /charts, /realitykit/presentationcomponent, /swiftui/chart3d
Skills: axiom-swiftui-performance, axiom-liquid-glass, axiom-swift-concurrency, axiom-app-intents-ref, axiom-swiftui-search-ref
Primary source WWDC 2025-256 "What's new in SwiftUI". Additional content from 2025-323 (Build a SwiftUI app with the new design), 2025-287 (Meet WebKit for SwiftUI), and Apple documentation.
Version iOS 26+, iPadOS 26+, macOS Tahoe+, watchOS 26+, visionOS 26+
WWDC: 2025-256, 2025-278(小组件新特性), 2025-287(认识SwiftUI的WebKit), 2025-310(使用工具优化SwiftUI性能), 2025-323(使用新设计构建SwiftUI应用), 2025-325(将Swift Charts带入三维空间), 2025-341(使用AttributedString在SwiftUI中打造富文本体验)
文档: /swiftui, /swiftui/defaulttoolbaritem, /swiftui/toolbarspacer, /swiftui/searchtoolbarbehavior, /swiftui/view/toolbar(id:content:), /swiftui/view/tabbarminimizebehavior(_:), /swiftui/view/tabviewbottomaccessory(isenabled:content:), /swiftui/slider, /swiftui/slidertick, /swiftui/slidertickcontentforeach, /webkit, /foundation/attributedstring, /charts, /realitykit/presentationcomponent, /swiftui/chart3d
技能: axiom-swiftui-performance, axiom-liquid-glass, axiom-swift-concurrency, axiom-app-intents-ref, axiom-swiftui-search-ref
主要来源 WWDC 2025-256 “SwiftUI新特性”。额外内容来自2025-323(使用新设计构建SwiftUI应用)、2025-287(认识SwiftUI的WebKit)和Apple文档。
版本 iOS 26+, iPadOS 26+, macOS Tahoe+, watchOS 26+, visionOS 26+