Highlight
iOS 17 的 UIKit 带来了 Xcode Preview 支持、viewIsAppearing 生命周期回调、自定义 Trait 系统、符号动画效果、空状态配置 API,以及 Collection View 性能翻倍等多项改进,让 UIKit 开发更接近 SwiftUI 的便捷体验。
核心内容
Xcode Preview 终于支持 UIKit
UIKit 开发者长期羡慕 SwiftUI 的实时预览。iOS 17 打破了这道墙——#Preview 宏直接支持 UIViewController 和 UIView。
你可以在预览中设置属性、填充数据,甚至批量测试不同配置。不需要 SwiftUI 做中间层,也不需要额外的包装代码。
viewIsAppearing:刚刚好
viewWillAppear 太早——视图还没加入层级,trait collection 还没更新,拿不到视图尺寸。viewDidAppear 太晚——它在独立的 CATransaction 中执行,修改要到动画结束才可见。
iOS 17 新增了 viewIsAppearing,在 viewWillAppear 之后、viewDidAppear 之前调用。此时视图已加入层级、已完成布局、trait collection 已更新。而且它只调用一次,不会像布局回调那样反复触发。
最实用的是它向后兼容到 iOS 13。
Trait 系统升级
UITraitCollection 以前只传播系统预定义的 trait(如 dark mode、size class)。iOS 17 允许定义自定义 trait,把自己的数据注入 trait 系统,自动传播到整个视图层级。
还新增了 trait 覆写 API 和闭包式变化监听,不再需要子类化来重写 traitCollectionDidChange。自定义 UIKit trait 还能桥接到 SwiftUI 的 environment key,两个框架的数据互通更顺畅。
空状态不再手写
每个应用都有空状态:首次启动没数据、网络断开、搜索无结果。以前这些都要自己拼 UI。
UIContentUnavailableConfiguration 提供了 .empty()、.loading()、.search() 三种预设。赋值给 view controller 的 contentUnavailableConfiguration 属性就生效。也可以用 UIHostingConfiguration 套一个 SwiftUI 视图进去。
配合 updateContentUnavailableConfiguration(using:) 方法,数据变化时调用 setNeedsUpdateContentUnavailableConfiguration(),空状态的显示和隐藏自动处理。
iPadOS 专属改进
Stage Manager 里的窗口拖拽区域扩大到整个 UINavigationBar。UIWindowSceneDragInteraction 可以给任意视图添加拖拽功能。
UISplitViewController 在 Stage Manager 中获得了更智能的列行为:自动隐藏 sidebar、窄宽度时用 overlay 或 displace 模式、宽了再切回 tile 模式。
UIDocumentViewController 成为文档应用的基础类,自动处理标题菜单、分享、拖拽、快捷键。UIDocument 遵循 UINavigationItemRenameDelegate,提供完整的重命名体验。
Apple Pencil 新增了 monoline pen、fountain pen、watercolor、crayon 四种笔刷。PencilKit 数据模型增加了 contentVersion 属性,用来处理新旧笔刷的兼容性问题。
其他增强
Collection View 性能大幅提升:一万个 item 的排序反转,iOS 17 比 iOS 16 快近一倍;删除一半 item 的更新快近三倍。无动画更新时性能提升更明显。
Compositional Layout 新增 uniformAcrossSiblings 尺寸,让同一组内的 self-sizing item 按最大高度对齐。
弹簧动画参数简化为 duration 和 bounce 两个值。UIView 新增了对应的动画方法,所有参数都是可选的,写个 animate 就能用系统默认弹簧效果。
状态栏样式现在会根据内容自动在 dark 和 light 之间切换,甚至支持左右分栏不同样式。很多手动控制状态栏样式的代码可以删掉了。
UIPageControl 支持分数进度显示,配合 UIPageControlTimerProgress 可以自动轮播,配合 UIPageControlProgress 可以手动同步视频进度。
Palette Menu 在 iOS 上可用,把菜单项水平排列,适合工具选择、颜色选择等场景。
详细内容
Xcode Preview 预览 UIViewController
(01:31)
class LibraryViewController: UIViewController {
// ...
}
#Preview("Library") {
let controller = LibraryViewController()
controller.displayCuratedContent = true
return controller
}
关键点:
#Preview宏直接支持返回 UIViewController- 可以在闭包里配置属性、填充数据
- 修改代码后预览自动刷新
Xcode Preview 预览 UIView
(01:48)
class SlideshowView: UIView {
// ...
}
#Preview("Memories") {
let view = SlideshowView()
view.title = "Memories"
view.subtitle = "Highlights from the past year"
view.images = ...
return view
}
关键点:
- 不需要 view controller,直接返回 UIView
- 适合独立视图组件的开发和调试
空状态配置
(08:19)
var config = UIContentUnavailableConfiguration.empty()
config.image = UIImage(systemName: "star.fill")
config.text = "No Favorites"
config.secondaryText =
"Your favorite translations will appear here."
viewController.contentUnavailableConfiguration = config
关键点:
.empty()创建空状态配置- 设置 image、text、secondaryText 即可
- 赋值给 view controller 的
contentUnavailableConfiguration自动显示
用 SwiftUI 自定义空状态
(08:56)
let config = UIHostingConfiguration {
VStack {
ProgressView(value: progress)
Text("Downloading file...")
.foregroundStyle(.secondary)
}
}
viewController.contentUnavailableConfiguration = config
关键点:
UIHostingConfiguration包裹 SwiftUI 视图- 适合需要复杂自定义布局的空状态
- 与 UIKit 的空状态 API 无缝集成
搜索空状态
(09:21)
override func updateContentUnavailableConfiguration(
using state: UIContentUnavailableConfigurationState
) {
var config: UIContentUnavailableConfiguration?
if searchResults.isEmpty {
config = .search()
}
contentUnavailableConfiguration = config
}
// Update search results for query
searchResults = backingStore.results(for: query)
setNeedsUpdateContentUnavailableConfiguration()
关键点:
.search()提供带搜索图标的预设空状态- 重写
updateContentUnavailableConfiguration(using:)响应数据变化 - 数据变化后调用
setNeedsUpdateContentUnavailableConfiguration()触发更新
核心启发
-
用 viewIsAppearing 替代 viewWillAppear 中的几何相关代码
- 做什么:把依赖视图尺寸、trait collection 的初始化逻辑从
viewWillAppear移到viewIsAppearing - 为什么值得做:
viewWillAppear时视图还没布局,拿到的尺寸是错的;viewIsAppearing时一切就绪,而且向后兼容到 iOS 13 - 怎么开始:找到所有在
viewWillAppear中读取view.bounds、traitCollection的代码,迁移到viewIsAppearing
- 做什么:把依赖视图尺寸、trait collection 的初始化逻辑从
-
统一应用的空状态体验
- 做什么:把应用里所有”暂无数据”、“加载中”、“搜索无结果”的界面换成
UIContentUnavailableConfiguration - 为什么值得做:三行代码获得系统一致的空状态样式,自动处理显示/隐藏,支持 SwiftUI 自定义
- 怎么开始:在 view controller 中重写
updateContentUnavailableConfiguration(using:),根据数据状态返回.empty()、.loading()或.search()
- 做什么:把应用里所有”暂无数据”、“加载中”、“搜索无结果”的界面换成
-
给关键操作加上符号动画反馈
- 做什么:在收藏、下载完成、蓝牙连接成功等操作时播放 symbol 动画
- 为什么值得做:
addSymbolEffect(.bounce)一行代码就能给用户清晰的视觉反馈,比弹窗更轻量 - 怎么开始:找到应用里用
UIAlertController提示简单成功状态的地方,换成 symbol image +addSymbolEffect(.bounce)
-
用 Xcode Preview 加速 UIKit 组件开发
- 做什么:给自定义 UIView 和 UIViewController 添加
#Preview - 为什么值得做:不用编译运行就能看到 UI 效果,调整布局、颜色、字体时即时反馈
- 怎么开始:在视图文件顶部加
#Preview { YourViewController() },配置必要的数据
- 做什么:给自定义 UIView 和 UIViewController 添加
-
优化 Collection View 的大数据量操作
- 做什么:检查应用里 Collection View 的大数据量更新,确认在 iOS 17 上的性能表现
- 为什么值得做:iOS 17 的 Collection View 排序快一倍、批量删除快三倍,无动画更新提升更大
- 怎么开始:用 Instruments 的 Time Profiler 测量现有代码,对比 iOS 16 和 iOS 17 的表现
关联 Session
- Unleash the UIKit trait system — 自定义 Trait 和 SwiftUI 环境键桥接的深入讲解
- Animate symbols in your app — SF Symbols 动画效果的完整指南
- What’s new with text and text interactions — 文本渲染和交互的新特性
- Support HDR images in your app — UIImageView HDR 支持的实现细节
评论
GitHub Issues · utterances