UIKit 的新特性
What's new in UIKit
2021年6月11日
一句话判断
UIKit 在 iOS 15 没有革命性变化,但拿到了一堆实用的小改进——UIButton 配置化、Sheet 自适应尺寸、系统按钮样式——这些改动加在一起让 UIKit 的日常开发体验上了一个台阶。
这场 Session 讲了什么
UIKit 作为 iOS 开发的老牌框架,在 iOS 15 获得了一系列渐进式改进。Session 覆盖了 UI 组件、SwiftUI 互操作和开发体验三个方面。
UI 组件方面,UIButton 新增了 UIButton.Configuration API,用声明式的方式配置按钮的样式、图标、标题和间距。替代了之前 button.titleLabel、button.imageView 的零散设置方式。系统提供了几种预设配置:.filled()、.tinted()、.gray()、.bordered() 等。
Sheet 展示方面,UISheetPresentationController 是新的 API,可以创建自适应尺寸的 Sheet——用户可以通过拖拽在半屏和全屏之间切换。这在地图 app、音乐 app 中已经被广泛使用。
UIListContentConfiguration 在 iOS 15 得到了增强,支持设置附属视图(accessory)的样式。配合 UICollectionLayoutListConfiguration,用 UICollectionView 构建列表的体验更接近 UITableView。
值得深挖的点
UIButton.Configuration 的状态驱动更新
UIButton.Configuration 是一个值类型(struct),你每次修改都会创建一个新的配置。配合 configurationUpdateHandler,你可以在按钮状态变化时(highlighted、disabled、selected)更新配置。这种方式比 setBackgroundColor(_:for:) 更灵活——你可以同时修改背景色、标题颜色、图标 tint 和阴影。
UISheetPresentationController 的 detent 系统
Sheet 的尺寸通过 detent(锚点)来定义。系统提供了 .medium()(大约半屏)和 .large()(全屏)两种预设 detent。你也可以自定义 detent 来设置特定的高度。用户可以在 detent 之间拖拽切换。
代码片段
// 使用 UIButton.Configuration 创建按钮
class LoginViewController: UIViewController {
func createLoginButton() -> UIButton {
var config = UIButton.Configuration.filled()
config.title = "登录"
config.subtitle = "使用 Apple ID"
config.image = UIImage(systemName: "applelogo")
config.imagePlacement = .leading
config.imagePadding = 8
config.baseBackgroundColor = .systemBlue
config.cornerStyle = .capsule
config.buttonSize = .large
let button = UIButton(configuration: config)
// 状态驱动的配置更新
button.configurationUpdateHandler = { button in
var config = button.configuration
switch button.state {
case .highlighted:
config?.baseBackgroundColor = .systemBlue.withAlphaComponent(0.8)
case .disabled:
config?.baseBackgroundColor = .systemGray
config?.title = "登录中..."
default:
config?.baseBackgroundColor = .systemBlue
}
button.configuration = config
}
return button
}
}
// 使用 UISheetPresentationController 创建自适应 Sheet
func presentSettingsSheet() {
let settingsVC = SettingsViewController()
if let sheet = settingsVC.sheetPresentationController {
// 设置可用的 detent
sheet.detents = [.medium(), .large()]
// 初始显示在半屏
sheet.selectedDetentIdentifier = .medium
// 显示 grabber(拖拽指示器)
sheet.prefersGrabberVisible = true
// 在 detent 变化时获取回调
sheet.delegate = self
}
present(settingsVC, animated: true)
}
// UISheetPresentationControllerDelegate
func sheetPresentationControllerDidChangeSelectedDetentIdentifier(
_ sheetPresentationController: UISheetPresentationController
) {
let isExpanded = sheetPresentationController.selectedDetentIdentifier == .large
print("Sheet 展开: \(isExpanded)")
}
// 使用 UICollectionLayoutListConfiguration 创建列表
func createListLayout() -> UICollectionViewCompositionalLayout {
var config = UICollectionLayoutListConfiguration(appearance: .insetGrouped)
// 配置滑动操作
config.leadingSwipeActionsConfigurationProvider = { indexPath in
let action = UIContextualAction(
style: .normal,
title: "置顶"
) { _, _, completion in
self.pinItem(at: indexPath)
completion(true)
}
action.backgroundColor = .systemBlue
return UISwipeActionsConfiguration(actions: [action])
}
return UICollectionViewCompositionalLayout.list(using: config)
}
最佳实践
UIButton.Configuration 是 iOS 15+ only 的 API。如果你的 app 需要支持 iOS 14,用 #available 检查后 fallback 到旧的 UIButton API。但长远来看,新的 Configuration API 是未来方向,建议新代码全部用 Configuration 写。
UISheetPresentationController 在紧凑宽度(iPhone)上表现为 Sheet,在常规宽度(iPad)上会自动变成 Popover。这个自适应行为很好,但如果你需要强制 Sheet 行为,设置 sheet.prefersEdgeAttachedInCompactHeight = true。
还有什么值得关注
UIActivityIndicatorView新增了.large样式,替代了旧的UIActivityIndicatorView.Style.whiteLarge。UINavigationBar的新 appearance API 支持设置标题的颜色和字体。UITextView支持了UISuggestionSelectionAPI,配合键盘的自动补全建议使用。