Highlight
iOS 15 引入
UIButtonConfiguration,用声明式配置替代逐属性设置,同时新增 toggle button、pop-up button 和UIMenu单选子菜单,让按钮从「能点」进化到「会表达状态」。
核心内容
按钮是每个 App 里最常见的控件,但 UIKit 的按钮 API 多年来一直比较原始。你要设置标题、图标、背景色、圆角、间距,每个属性都要单独调。状态变化时还得手动更新样式,代码分散在各处。
iOS 15 用 UIButtonConfiguration 彻底重构了按钮系统。核心思路是:把按钮的所有视觉属性打包成一个值类型配置,通过修改配置来改变按钮外观。系统提供四种预设样式(plain、gray、tinted、filled),支持标题+副标题+图标组合、动态类型、多行文本,还能显示加载指示器。
更实用的是新增了两种按钮行为:toggle button(像开关一样切换选中状态)和 pop-up button(点击弹出菜单并显示当前选中项)。这两种行为都基于 UIAction 和 UIMenu,不需要额外写代理方法。
用 Configuration 创建按钮
(02:13)
最简单的入门方式:给一个现有按钮赋值配置。
let signInButton = UIButton(type: .system)
signInButton.configuration = .filled()
signInButton.setTitle("Sign In", for: [])
关键点:
UIButton会自动把用旧 API(如setTitle)设置的标题和图标整合到 configuration 中- 这意味着可以逐步迁移,不需要一次性重写所有按钮代码
定制 Configuration
(03:20)
用结构体方式配置按钮,所有属性一次设好:
var config = UIButton.Configuration.tinted()
config.title = "Add to Cart"
config.image = UIImage(systemName: "cart.badge.plus")
config.imagePlacement = .trailing
addToCartButton = UIButton(configuration: config, primaryAction: ...)
关键点:
imagePlacement控制图标在文字的哪一侧(.leading或.trailing)- 系统会自动处理图标和文字之间的间距
状态驱动的动态更新
(04:45)
按钮被按下时图标从线框变实心,这个效果用 configurationUpdateHandler 实现:
addToCartButton.configurationUpdateHandler = { [unowned self] button in
var config = button.configuration
config?.image = button.isHighlighted
? UIImage(systemName: "cart.fill.badge.plus")
: UIImage(systemName: "cart.badge.plus")
config?.subtitle = self.itemQuantityDescription
button.configuration = config
}
关键点:
configurationUpdateHandler在按钮状态变化时自动调用- 闭包内先读取当前配置,修改后再赋值回去
isHighlighted、isSelected、isEnabled等状态变化都会触发
外部数据变化时手动触发更新:
private var itemQuantityDescription: String? {
didSet {
addToCartButton.setNeedsUpdateConfiguration()
}
}
关键点:
setNeedsUpdateConfiguration()标记按钮需要更新- 系统会在下一个运行循环调用
configurationUpdateHandler
完全自定义样式
(08:26)
用 UIBackgroundConfiguration 精细控制背景:
var config = UIButton.Configuration.filled()
config.buttonSize = .large
config.image = UIImage(systemName: "cart.fill")
config.title = "Checkout"
config.background.backgroundColor = .buttonEmporium
let checkoutButton = UIButton(configuration: config, primaryAction: ...)
checkoutButton.configurationUpdateHandler = { [unowned self] button in
var config = button.configuration
config?.showsActivityIndicator = self.isCartBusy
button.configuration = config
}
关键点:
buttonSize有.small、.medium、.large三种预设showsActivityIndicator为 true 时显示加载动画,必要时替换图标background.backgroundColor设置自定义背景色,系统仍会自动处理按下和禁用状态的变暗效果
Toggle Button
(11:56)
Toggle button 每次点击自动切换 isSelected 状态,行为类似 UISwitch 但更紧凑:
let stockToggleAction = UIAction(title: "In Stock Only") { _ in
toggleStock()
}
let button = UIButton(primaryAction: stockToggleAction)
button.changesSelectionAsPrimaryAction = true
button.isSelected = showingOnlyInStock()
关键点:
changesSelectionAsPrimaryAction = true开启 toggle 行为- 点击时系统自动翻转
isSelected - 可以在
configurationUpdateHandler中根据isSelected改变外观 UIBarButtonItem也新增了isSelected属性,支持同样的 toggle 行为
Pop-up Button
(14:30)
Pop-up button 点击弹出菜单,选中某项后按钮标题自动更新为选中项:
let colorClosure = { (action: UIAction) in
updateColor(action.title)
}
let button = UIButton(primaryAction: nil)
button.menu = UIMenu(children: [
UIAction(title: "Bondi Blue", handler: colorClosure),
UIAction(title: "Flower Power", state: .on, handler: colorClosure)
])
button.showsMenuAsPrimaryAction = true
button.changesSelectionAsPrimaryAction = true
// 读取当前选中项
updateColor(button.menu?.selectedElements.first?.title)
// 程序化设置选中项
(button.menu?.children[selectedColorIndex()] as? UIAction)?.state = .on
关键点:
showsMenuAsPrimaryAction = true让点击直接弹出菜单changesSelectionAsPrimaryAction = true让按钮变成 pop-up 行为- 按钮标题和图标会自动显示当前选中项
selectedElements始终返回一个元素(单选约束)
单选子菜单
(18:18)
在 pull-down 菜单里嵌套单选子菜单,比如排序选项:
let sortMenu = UIMenu(title: "Sort By", options: .singleSelection, children: [
UIAction(title: "Title", handler: sortClosure),
UIAction(title: "Date", handler: sortClosure),
UIAction(title: "Size", handler: sortClosure)
])
let topMenu = UIMenu(children: [
UIAction(title: "Refresh", handler: refreshClosure),
UIAction(title: "Account", handler: accountClosure),
sortMenu
])
let sortSelectionButton = UIBarButtonItem(primaryAction: nil, menu: topMenu)
关键点:
options: .singleSelection让子菜单变成单选模式- 选中行为自动管理,不需要手动维护 state
- 嵌套子菜单也适用同样的单选约束
详细内容
四种预设样式
iOS 15 提供四种基础按钮样式:
| 样式 | 外观 | 适用场景 |
|---|---|---|
| plain | 无背景,文字+图标 | 工具栏、导航栏 |
| gray | 灰色圆角背景 | 次要操作 |
| tinted | 带主题色的透明背景 | 强调但不过于突出 |
| filled | 实心填充主题色 | 主要操作 |
布局控制
UIButton.Configuration 提供精细的布局控制:
var config = UIButton.Configuration.filled()
config.contentInsets = NSDirectionalEdgeInsets(top: 8, leading: 16, bottom: 8, trailing: 16)
config.imagePadding = 8
config.titlePadding = 4
config.titleAlignment = .leading
config.imagePlacement = .top
关键点:
contentInsets控制按钮内容区与边界的间距imagePadding控制图标与文字之间的间距titlePadding控制多行标题之间的间距imagePlacement支持.leading、.trailing、.top、.bottom
Mac Catalyst 自动适配
(15:43)
新按钮类型在 Mac Catalyst 上会自动转换为 Mac 原生样式:
- Pull-down button 变成 Mac 的 bezeled pull-down 按钮
- Pop-up button 变成 Mac 的 pop-up 按钮
- Toggle button 变成 Mac 的 toggle 按钮
如果希望保持 iPad 风格(比如突出的自定义按钮),可以设置:
checkoutButton.behaviorStyle = .ipad
核心启发
-
替换所有自定义按钮子类
- 做什么:把项目里继承
UIButton写的自定义按钮类,逐步迁移到UIButtonConfiguration - 为什么值得做:以前为了做「带图标的圆角按钮」或「按下变色的按钮」,很多人写了
CustomButton: UIButton。现在系统原生支持,代码从几十行降到几行 - 怎么开始:找一个最简单的自定义按钮,用
.filled()或.tinted()配置替换,验证外观一致后批量迁移
- 做什么:把项目里继承
-
用 Toggle Button 替代 UISwitch
- 做什么:在设置页、筛选条件等场景,把
UISwitch换成带文字标签的 toggle button - 为什么值得做:
UISwitch只有开关状态没有文字说明,用户不知道切换的是什么。Toggle button 把标签和控件合二为一,节省空间且语义更清晰 - 怎么开始:
changesSelectionAsPrimaryAction = true+ 在configurationUpdateHandler里根据isSelected切换图标和颜色
- 做什么:在设置页、筛选条件等场景,把
-
用 Pop-up Button 替代 Segmented Control
- 做什么:当选项超过 3-4 个时,用 pop-up button 替代
UISegmentedControl - 为什么值得做:Segmented control 在选项多时会截断文字或溢出屏幕。Pop-up button 不受宽度限制,还能显示层级结构
- 怎么开始:创建
UIMenu+showsMenuAsPrimaryAction = true+changesSelectionAsPrimaryAction = true
- 做什么:当选项超过 3-4 个时,用 pop-up button 替代
-
做带加载状态的提交按钮
- 做什么:表单提交按钮在请求过程中显示加载动画,请求完成后恢复
- 为什么值得做:
showsActivityIndicator一行代码实现,系统会自动处理图标替换和动画,不需要手动管理UIActivityIndicatorView - 怎么开始:在
configurationUpdateHandler里根据请求状态设置config?.showsActivityIndicator = isLoading
-
统一 App 内所有按钮风格
- 做什么:定义一套 App 级别的按钮配置工厂方法,所有按钮通过工厂创建
- 为什么值得做:
UIButtonConfiguration是值类型,可以复用和组合。定义primaryButtonConfig()、secondaryButtonConfig()等工厂方法,确保全 App 按钮风格一致 - 怎么开始:写一个
ButtonStyle枚举,每个 case 返回预配置好的UIButton.Configuration
关联 Session
- What’s new in UIKit — UIKit 年度新特性总览
- Build interfaces with style — 在 Interface Builder 中使用新按钮系统
- What’s new in Mac Catalyst — Mac Catalyst 的新特性,包含按钮自动适配
评论
GitHub Issues · utterances