System & Services 进阶 22m
增强你的 Intent 能力
Empower your intents
2020年6月25日
一句话判断
这场 Session 讲了如何让你的自定义 Intent 更强大——动态选项、参数联动、条件显示,让你的快捷指令不再是”填参数 → 执行”的死板流程。
这场 Session 讲了什么
自定义 Intent 的用户体验在很大程度上取决于参数的配置方式。2020 年的改进让 Intent 的参数交互更加灵活和智能。
核心更新包括:动态选项(Dynamic Options)——参数的可选值不再是固定的,可以根据上下文动态生成。比如”选择播放列表”参数可以实时从用户的账户中获取列表。参数联动(Parameter Linking)——当用户选择某个参数后,可以影响其他参数的显示和可选值。比如选择”摇滚”类型后,“艺术家”参数自动过滤为摇滚艺术家。条件显示(Conditional Display)——某些参数只在特定条件下才显示,避免界面过于复杂。
Session 还介绍了新的 Intent 编辑器功能,可以在 Xcode 中直接预览和测试 Intent 的交互流程。
值得深挖的点
动态选项的实现。 过去参数的选项列表是在 Intent 定义文件中静态配置的。现在你可以通过 INIntentHandlerProviding 协议动态提供选项。关键是在 provideXxxOptionsCollection(for:with:) 方法中根据当前上下文返回合适的选项列表。
参数联动的用户体验。 联动的关键是让用户感觉选项是”自然的”而非”受限的”。当你选择了一个城市后,餐厅列表自动更新为该城市的餐厅——这不是限制,而是帮助用户更快找到想要的。设计联动时要注意:联动方向应该是从宽到窄(先选大类再选小类),不要反向联动。
代码片段
import Intents
// 动态提供参数选项
class OrderCoffeeIntentHandler: NSObject, OrderCoffeeIntentHandling {
// 动态提供咖啡店列表
func provideStoreOptionsCollection(
for intent: OrderCoffeeIntent,
with completion: @escaping (INObjectCollection<CoffeeStore>, Error?) -> Void
) {
// 从网络或本地数据获取咖啡店列表
fetchStores { stores in
let options = stores.map { store in
CoffeeStore(
identifier: store.id,
display: store.name
)
}
completion(INObjectCollection(items: options), nil)
}
}
// 根据选择的咖啡店动态提供菜单
func provideMenuOptionsCollection(
for intent: OrderCoffeeIntent,
with completion: @escaping (INObjectCollection<CoffeeItem>, Error?) -> Void
) {
guard let storeId = intent.store?.identifier else {
completion(INObjectCollection(items: []), nil)
return
}
fetchMenu(storeId: storeId) { items in
let options = items.map { item in
CoffeeItem(
identifier: item.id,
display: "\(item.name) - ¥\(item.price)"
)
}
completion(INObjectCollection(items: options), nil)
}
}
}
// 参数验证和确认
func confirm(intent: OrderCoffeeIntent, completion: @escaping (OrderCoffeeIntentResponse) -> Void) {
// 验证参数是否完整
guard intent.store != nil, intent.menuItem != nil else {
completion(OrderCoffeeIntentResponse(code: .failure, userActivity: nil))
return
}
// 检查咖啡店是否营业
checkStoreAvailability(storeId: intent.store!.identifier!) { isAvailable in
if isAvailable {
completion(OrderCoffeeIntentResponse(code: .ready, userActivity: nil))
} else {
completion(OrderCoffeeIntentResponse(
code: .failureRequiringAppLaunch,
userActivity: nil
))
}
}
}
最佳实践
- 动态选项要有合理的默认值。 第一次打开参数选择器时,如果列表为空用户会困惑。提供最近使用或最热门的选项作为默认。
- 参数联动要清晰可预测。 用户改变一个参数后,应该能理解为什么其他参数也变了。在 Intent 定义中用 description 解释联动逻辑。
- 动态选项的加载要快。 Shortcuts App 中参数选项加载慢会严重影响体验。使用缓存和预加载。
- 为选项提供 subtitle 和 image。 纯文字列表单调且难以区分,加上描述和图标让选项更直观。
还有什么值得关注
- INObject 支持 subtitle 属性,可以显示额外的描述信息
- 选项支持分组显示(通过 INObjectCollection 的 sections)
- 新的 Intent 编辑器支持实时预览参数联动效果
- 自定义 Intent 的响应支持富文本展示
WWDC 2020