UIKit 新特性
What's new in UIKit
2022年6月6日
一句话判断
如果你还在用 UIMenuController 和老式的 cell 高度计算方式,iOS 16 是时候动手迁移了——这版 UIKit 把过去几年积压的 API 债务清理了不少。
这场 Session 讲了什么
iOS 16 对 UIKit 做了大量细节层面的打磨。最显眼的变化是 UIPageControl 终于支持了自定义指示器样式,你可以用 UIPageControl.TimerProgress 做进度指示,也可以用 UIPageControl.InfiniteProgress 做无限滚动场景。这个改动虽小,但解决了长期以来开发者只能用第三方库的尴尬。
Self-sizing cells 在 iOS 16 中得到了显著提升。UICollectionView 的 self-sizing 现在更加高效,Auto Layout 在计算 cell 高度时的性能损耗大幅降低。配合新的 UICollectionView.CellRegistration 和 UIListContentConfiguration,构建动态高度的列表变得轻松很多。
编辑菜单方面,UIMenuController 被正式标记为废弃,取而代之的是 UIEditMenuInteraction。新 API 基于 UIContextMenuInteraction 的设计理念,支持位置感知的菜单弹出,在 iPad 上体验尤其明显。除此之外,UISingleSelection 样式的加入让 UITableViewCell 的选中状态配置更直观,不再需要手动管理 accessoryType。
值得深挖的点
Self-sizing Cell 性能优化背后的机制变化
iOS 16 之前,UICollectionView 的 self-sizing cell 在滚动时会频繁触发布局计算,导致帧率下降。新版本引入了估算高度的缓存机制——系统会记住第一次计算出的高度,后续滚动直接复用,只有数据源变化时才重新计算。这意味着 prefetchDataSource 配合 self-sizing 终于不会卡顿了。如果你的列表包含富文本或异构 cell,升级到 iOS 16 后性能提升肉眼可见。
UIEditMenuInteraction 与 UIMenuController 的迁移策略
UIEditMenuInteraction 的设计哲学和 UIContextMenuInteraction 一脉相承:通过 delegate 方法控制菜单内容,通过 UITargetedPreview 控制动画锚点。但它的 API 更简洁——你不需要手动处理 canPerformAction 和 UIMenuControllerWillShowMenu 通知。迁移时最需要注意的是:新 API 的菜单内容由 editMenuInteraction(_:menuFor:) -> UIMenu 提供,UIMenu 的 children 里放 UIAction 即可。不再需要 @objc selector 那套。
代码片段
UIPageControl 自定义进度样式
// 使用计时器进度样式——适合音频/视频播放场景
let pageControl = UIPageControl()
pageControl.numberOfPages = 5
pageControl.currentPage = 0
pageControl.backgroundStyle = .prominent // 半透明背景,在深色内容上更清晰
// 设置进度指示器
let progress = UIPageControl.TimerProgress()
progress.progress = 0.6
pageControl.progress = progress
UIEditMenuInteraction 替代 UIMenuController
// 创建编辑菜单交互
let editMenuInteraction = UIEditMenuInteraction(delegate: self)
textView.addInteraction(editMenuInteraction)
// 长按或点击时弹出菜单
@objc func handleLongPress(_ gesture: UILongPressGestureRecognizer) {
let point = gesture.location(in: textView)
let config = UIEditMenuConfiguration(identifier: nil, sourcePoint: point)
editMenuInteraction.presentEditMenu(with: config)
}
// 实现代理方法提供菜单内容
func editMenuInteraction(
_ interaction: UIEditMenuInteraction,
menuFor configuration: UIEditMenuConfiguration,
suggestedActions: [UIMenuElement]
) -> UIMenu {
// suggestedActions 包含系统默认的复制/粘贴/剪切等操作
let customAction = UIAction(title: "翻译") { _ in
// 自定义操作
}
return UIMenu(children: suggestedActions + [customAction])
}
UISingleSelection 选中样式
// iOS 16 新的选中配置方式
let registration = UITableViewCell.Registration(
handler: { cell, indexPath, item in
var content = cell.defaultContentConfiguration()
content.text = item.title
cell.contentConfiguration = content
// 设置单选样式——自动管理选中指示器
cell.selectionStyle = .none
}
)
// 在 UITableView 配置中使用
var snapshot = dataSource.snapshot()
snapshot.select([selectedItem]) // 直接选中,无需手动 reload
最佳实践
迁移 UIMenuController 到 UIEditMenuInteraction 时,不要试图兼容两个版本。UIEditMenuInteraction 从 iOS 16 开始可用,如果你还支持 iOS 15,用 if #available 做分支处理,旧版本保留 UIMenuController 逻辑即可。新项目中直接用新 API,省去日后迁移的麻烦。
Self-sizing cell 在 iOS 16 上终于可用了,但别滥用。如果你的 cell 结构简单、高度可预测,手动计算高度仍然是更高效的选择。self-sizing 真正的价值在于内容高度无法预知的场景——比如服务端下发的富文本、用户生成的评论等。
UIPageControl 的 backgroundStyle 属性有三个选项:.default、.prominent、.minimal。如果你的页面背景颜色多变或包含图片,选 .prominent,它在任何背景上都能保持可见性。
还有什么值得关注
UINavigationItem新增了backAction属性,可以自定义返回按钮的行为而不需要创建自定义UIBarButtonItem。UIPasteControl提供了系统级的粘贴按钮样式,适合配合 iOS 16 的隐私粘贴权限使用。UISheetPresentationController新增了detents的自定义UISheet.Detent,可以指定精确高度而非仅限medium和large。