3、UIAccessibility协议:核心属性
好,咱们今天聊聊UIAccessibility协议。说实话,这个协议是iOS无障碍开发的基石。你写的每一个控件,只要想被旁白(VoiceOver)识别,就得遵守这个协议。UIKit里大部分组件默认都实现了,但自定义控件嘛……嗯,就得我们自己动手了。
我个人习惯把UIAccessibility协议比作「控件的自我介绍」。你想想看,一个看不见屏幕的用户,怎么知道这个按钮是干嘛的?全靠这几个属性告诉系统。今天咱们就把最核心的四个属性讲透:accessibilityLabel、accessibilityHint、accessibilityValue、accessibilityTraits。
3.1 accessibilityLabel:控件的名字
这是最重要的属性,没有之一。旁白用户触摸到控件时,第一个听到的就是label。它应该是一个简短、精准的名词或短语。
核心原则:label回答的是「这是什么?」
我在项目中遇到过不少坑。比如一个「发送」按钮,有人直接写成了「点击这里发送消息」。其实没必要。旁白用户已经知道这是个按钮(traits会告诉它),你只需要说「发送」就够了。
// 正确示例
button.accessibilityLabel = "发送"
// 错误示例
button.accessibilityLabel = "点击这里发送消息" // 冗余!
还有一个常见问题:图片按钮。比如一个垃圾桶图标,代表删除操作。这时候label应该写「删除」,而不是「垃圾桶图标」。
小技巧:label不要包含控件类型。比如「发送按钮」里的「按钮」二字是多余的,因为traits会告诉用户这是个按钮。
3.2 accessibilityHint:操作的提示
Hint是label的补充。它回答的是「操作后会怎样?」。注意,不是所有控件都需要hint。只有当操作结果不明确时,才需要加。
我曾经接手过一个项目,所有按钮都加了hint,结果旁白用户反馈说「太啰嗦了,每个按钮都要听两句话」。所以,hint要克制使用。
// 需要hint的场景
let deleteButton = UIButton()
deleteButton.accessibilityLabel = "删除"
deleteButton.accessibilityHint = "将永久移除这条记录"
// 不需要hint的场景
let submitButton = UIButton()
submitButton.accessibilityLabel = "提交"
// 提交按钮的操作结果很明显,不需要hint
注意:hint不要重复label的内容。比如label是「保存」,hint写成「点击保存」就完全多余了。
3.3 accessibilityValue:控件的当前状态
这个属性主要用于那些有状态的控件。比如滑块、进度条、开关、自定义分段控件等。它回答的是「当前值是多少?」。
举个例子,一个音量滑块。label是「音量」,value就应该是「50%」或者「0.5」。系统自带的UISlider会自动处理,但自定义的就得我们自己来了。
// 自定义滑块
class CustomSlider: UISlider {
override var accessibilityValue: String? {
get {
// 返回百分比格式
return "\(Int(self.value * 100))%"
}
set {
// 通常不需要设置
}
}
}
我记得有一次做一个自定义分段控件,三个选项分别是「低」「中」「高」。如果不设置value,旁白只会读「低 按钮」。设置了value之后,它会读「低,当前选中」。用户体验完全不一样。
| 控件类型 | label示例 | value示例 |
|---|---|---|
| UISlider | 音量 | 50% |
| UIProgressView | 下载进度 | 75% |
| UISwitch | Wi-Fi | 开 / 关 |
| 自定义分段控件 | 难度选择 | 高,当前选中 |
3.4 accessibilityTraits:控件的「身份」
Traits是最容易被忽视的属性。它告诉旁白这个控件是什么类型、有什么交互行为。说白了,就是给控件贴标签。
系统默认的traits已经覆盖了大部分场景:
.button— 按钮.image— 图片.staticText— 静态文本.searchField— 搜索框.adjustable— 可调节(如滑块).selected— 选中状态.header— 标题
我见过最典型的错误:自定义一个可点击的视图,忘了设置traits。结果旁白用户摸上去,系统只读label,不提示「可点击」。用户根本不知道能操作。
// 自定义可点击视图
class TapableView: UIView {
override var accessibilityTraits: UIAccessibilityTraits {
get {
return .button // 告诉系统这是个按钮
}
set {
// 保持默认
}
}
}
重要:traits可以组合使用。比如一个选中的按钮:[.button, .selected]。旁白会读「某某按钮,已选中」。
3.5 四个属性的配合使用
光讲单个属性没意思,咱们看看它们怎么配合。我拿一个自定义的「收藏」按钮举例:
class FavoriteButton: UIButton {
var isFavorited: Bool = false {
didSet {
// 更新无障碍属性
updateAccessibility()
}
}
private func updateAccessibility() {
// label:告诉用户这是什么
accessibilityLabel = "收藏"
// value:告诉用户当前状态
accessibilityValue = isFavorited ? "已收藏" : "未收藏"
// traits:告诉用户这是个按钮,且可切换
accessibilityTraits = [.button, .selected]
// hint:告诉用户操作结果
accessibilityHint = isFavorited ? "点击取消收藏" : "点击添加收藏"
}
}
你看,四个属性各司其职。旁白用户触摸时会听到:「收藏,未收藏,按钮,已选中。点击添加收藏」。信息完整,不啰嗦。
避坑指南:我曾经把label和value写反了。label写了「已收藏」,value写了「收藏」。结果旁白读成了「已收藏,收藏」。用户完全懵了。记住:label是名字,value是状态。
3.6 调试技巧
最后分享几个调试方法。我平时最常用的是「旁白转子」里的「语言」设置,可以调慢语速,仔细听每个属性的朗读顺序。
还有一个神器:Xcode的Accessibility Inspector。打开它,选中你的控件,所有无障碍属性一目了然。哪个没设置、哪个设置错了,一眼就能看出来。
- 打开Xcode → Open Developer Tool → Accessibility Inspector
- 选择模拟器或真机
- 点击控件,查看右侧面板
嗯,这四个属性讲完了。说白了,就是让系统知道「这是什么」「当前值多少」「能怎么操作」「操作后怎样」。下次你写自定义控件时,不妨按这个思路走一遍。用户会感谢你的。