用 UIKit 构建新设计系统应用
Build a UIKit app with the new design
2025年6月9日
一句话判断
Liquid Glass 不是换个背景色就完事的——它改变了 UIKit 组件的行为逻辑:Tab Bar 浮起来了、导航栏变透明了、搜索栏可以塞进工具栏了、按钮要分组共享玻璃背景了。好消息是重新编译就有大部分效果,坏消息是你之前做的一切 UI 自定义都得重新审视。
这场 Session 讲了什么
全面拆解 iOS 26 新设计在 UIKit 层面的适配方案,覆盖六个领域。
Tab View 和 Split View:Tab Bar 浮在内容上方,支持滚动时自动最小化(tabBarMinimizeBehavior)。新增 UITabAccessory 用于在 Tab Bar 上方放辅助视图(比如音乐 App 的迷你播放器),最小化时辅助视图动画内嵌到 Tab Bar。iPad 上 Tab Bar 和 SideBar 都用 Liquid Glass,新增 UIBackgroundExtensionView 让艺术图无缝延伸到 SideBar 后面。
导航栏和工具栏:透明背景 + Liquid Glass 按钮。系统自动按规则分组按钮——图片按钮共享玻璃背景,文字按钮和”Done/Close”按钮独占背景。用 fixedSpace 可以手动拆分组。新增 subtitle API 和 largeSubtitleView。大标题现在放在内容滚动视图顶部,和内容一起滚动。
Presentation:动态 Zoom Transition——从玻璃按钮发起的菜单/弹窗,按钮会变形融入弹窗。Action Sheet 在 iPhone 上也锚定到源视图(和 iPad 一致),不用源视图时居中显示并带取消按钮。
搜索:iPhone 上搜索栏自动移到工具栏。iPad 上可集成到导航栏尾部(类似 macOS)。Tab Bar 支持专门的 Search tab,点击展开为搜索字段。
控件:Glass 按钮(.glass() 和 .prominentGlass())。Slider 支持刻度标记(tick marks)、中性值锚点、无滑块样式。Switch 和 Segmented Control 的拇指有 Liquid Glass 交互效果。
自定义 Glass:UIGlassEffect + UIVisualEffectView。支持 tint color、isInteractive 交互效果、自动明暗模式切换。UIGlassContainerEffect 让多个 Glass 视图在靠近时自动融合。
值得深挖的点
按钮分组的自动规则需要理解透
系统不是随便分组的。规则是:图片按钮之间共享背景;多项目组(buttonGroup)共享背景;文字按钮、系统”Done/Close”、prominent 样式按钮独占背景。这个规则在大部分情况下是合理的,但如果你想要更细粒度的控制,要用 fixedSpace 手动拆分。
一个反模式是用 flexibleSpace 均匀分布工具栏按钮——默认情况下每个 flexibleSpace 都会分隔背景。如果想保持分组但均匀分布,必须设置 hidesSharedBackground = false。
BackgroundExtensionView 的正确用法
这个组件解决的是”艺术图延伸到 SideBar 后面”的问题。输入是一个 contentView(通常是 UIImageView),它会被自动延伸填充 Safe Area 之外的空白区域。关键是:延伸只发生在 Safe Area inset 为正的边缘(顶部的导航栏、Leading 的 SideBar)。
不能延伸的内容(比如节目描述文字)要作为 extensionView 的兄弟视图而不是子视图——否则也会被延伸效果影响。如果不需要顶部延伸(比如导航栏内容很少),可以关掉自动布局(automaticallyPlacesContentView = false)手动定位 contentView。
UIGlassContainerEffect 的融合动画
多个 Glass 视图放在同一个 UIGlassContainerEffect 容器里时,靠近会像水滴一样融合,重叠会合并成一个形状。用 spacing 属性控制开始融合的距离。如果要拆分,先把它们放在同一位置不动画,然后一起动画离开。
这个效果的关键约束是:容器会强制所有子 Glass 视图使用统一的自适应策略——即使它们在不同背景下,外观也保持一致。
代码片段
Tab Bar 滚动最小化 + 辅助视图
// 设置 Tab Bar 滚动行为
tabBarController.tabBarMinimizeBehavior = .onScrollDown
// 添加迷你播放器到 Tab Bar 上方
let miniPlayer = MiniPlayerView()
let accessory = UITabAccessory(contentView: miniPlayer)
tabBarController.bottomAccessory = accessory
// 监听辅助视图内嵌状态
func updateMiniPlayer() {
let env = traitCollection.tabAccessoryEnvironment
if env.isInline {
// 内嵌模式:隐藏部分控件
miniPlayer.hideSecondaryControls()
} else {
miniPlayer.showSecondaryControls()
}
}
坑:TabAccessoryEnvironment 的变化需要通过 traitCollection 观察,不要自己写定时器去检测。
自定义 Glass 按钮
func createGlassButton(title: String, tint: UIColor?) -> UIButton {
let button = UIButton()
button.setTitle(title, for: .normal)
var config = UIButtonConfiguration.glass()
if let tint {
config = UIButtonConfiguration.prominentGlass()
button.tintColor = tint
}
button.configuration = config
return button
}
// 多个 Glass 视图融合
let container = UIVisualEffectView(effect: UIGlassContainerEffect(spacing: 12))
let button1 = createGlassButton(title: "Share", tint: nil)
let button2 = createGlassButton(title: "Info", tint: nil)
container.contentView.addSubview(button1)
container.contentView.addSubview(button2)
// 靠近时自动融合
坑:Glass 的 materialize/dematerialize 动画只在设置 effect 属性时触发——用 alpha 隐藏/显示不会触发这个动画。始终通过设置 effect 为 nil 来移除 Glass。
搜索栏集成到工具栏
// iPhone: 搜索栏放在工具栏
let searchItem = UIBarButtonItem(searchBarPlacement: searchBar)
navigationItem.rightBarButtonItems = [searchItem, shareItem]
// iPad: 搜索栏集成到导航栏尾部
navigationItem.searchBarPlacementAllowsExternalIntegration = true
// Tab Bar 的 Search Tab
let searchTab = UITab(title: "Search", image: UIImage(systemName: "magnifyingglass"),
viewController: searchVC)
searchTab.automaticallyActivateSearch = true
tabBarController.tabs = [homeTab, libraryTab, searchTab]
坑:searchBarPlacementBarButtonItem 会根据可用空间自动决定是展开为搜索字段还是显示为工具栏按钮——不要手动控制这个行为。
最佳实践
第一步永远是重新编译。大部分 Liquid Glass 效果会在编译后自动生效——Tab Bar 浮起、导航栏透明、控件新外观。不要急着改代码,先看看自动适配的效果再决定哪些需要调整。
清理旧的自定义样式。如果你之前用了 UIBarAppearance 或 backgroundColor 自定义导航栏/工具栏外观,现在要移除它们——它们会干扰 Glass 效果。同样,之前为了区分按钮而设置的自定义颜色,现在系统自动处理分组了,手动设置可能适得其反。
Scroll Edge Effect 是新的关键概念。滚动视图在导航栏/工具栏下方会自动应用视觉处理以保证可读性。如果你有自定义的浮动容器覆盖在滚动视图边缘,用 ScrollEdgeElementContainerInteraction 把 edge effect 插到浮动容器后面——否则浮动容器下方的文字会不可读。
还有什么值得关注
- Slider 的 TrackConfiguration 支持 allowsTickValuesOnly——限制只能选刻度值,适合离散选项(音量等级、速度档位)。
- Slider 的 neutralValue 可以锚定在任意位置(不一定是最小值),填充色显示当前值和中性值的差值。
- 无滑块样式(thumbless)的 Slider 看起来像进度条,适合媒体播放时不分散注意力。
- 动态 Zoom Transition 从 iOS 16 的 Zoom Transition 演化而来,现在 Glass 按钮作为发起源时按钮会变形融入弹窗——这是自动的,不需要额外代码。
- UIGlassEffect 的 cornerConfiguration 支持 .containerRelative,自动根据与容器边角的距离调整圆角半径——保持同心圆角。