当前位置: 首页 > news >正文

iOS Widget 开发-9:可配置 Widget:使用 IntentConfiguration 实现参数选择

iOS Widget 支持通过“参数化”配置内容,让用户在添加 Widget 时根据个人偏好选择展示内容。这一功能通过 IntentConfiguration 实现,是打造个性化、可复用小组件的关键。
本篇文章介绍如何使用 IntentConfiguration(基于 .intentdefinition)为 Widget 提供可配置参数——以“选择蔬菜”为示例,讲解从创建 .intentdefinition 文件、生成代码、在 Widget 中使用到实现动态选项(Intents Extension)的完整流程,并给出调试与注意事项。

1. 概念回顾:IntentConfiguration 是什么

  • IntentConfiguration 是 WidgetKit 提供给第三方参数配置的经典方式,基于 Intents 框架。使用时,Widget 编辑界面会自动呈现意图中定义的参数选择器。
  • 主要三种 WidgetConfiguration:
    • StaticConfiguration:无配置项,固定展示内容。
    • IntentConfiguration:基于 .intentdefinition(Intents),通过 Intent 文件或 Intents extension 提供选项。
    • AppIntentConfiguration:iOS 16 引入的基于 AppIntents 的现代方式(更推荐在 iOS 16+ 环境使用)。

何时使用 IntentConfiguration:需要支持 iOS 14/15 的项目,或者已有 .intentdefinition 工作流,需要兼容旧系统。


2. 在 Xcode 中创建 .intentdefinition(静态选项)

以下以“选择蔬菜(SelectVegetableIntent)”为例:

在 Project Navigator 中,选择项目或 Widget 的 group,选择 File → New → File from Template
在模板列表选择 Resource 下的 SiriKit Intent Definition file.
请添加图片描述

点击 Next,命名为 VegetableCategories.intentdefinitionIntentDefinitions.intentdefinition,注意两个 target 都要勾选。

请添加图片描述

打开该文件,左下角点击 +,选择 New Intent,命名为 VegetableCategories(或你喜欢的名称)。

在这个文件里面,需要更改几个地方,将 Category 设置为 View,顺便将 Description 写一下,记得把 Intent is eligible for widgets 勾选上。
请添加图片描述

现在点击左下角+号创建一个 Enum, 命名为 Vegetable,并在 Cases 中添加一些类型。

在这里插入图片描述

现在选择刚才创建的 VegetableCategories intent,在 Parameters 中创建一个属性 vegetable,并选择类型为刚才创建的 enum。

请添加图片描述
到此,一个简单的 intentdefinition 文件就创建完了。
那么在项目中如何使用呢?

选中主工程的 target,在 General 下面的 Supported Intents 中添加刚才创建的 VegetableCategoriesIntent。

请添加图片描述
回到我们 Widget extension 中,修改代码如下:

struct VegetableCategoriesProvider: IntentTimelineProvider {func placeholder(in context: Context) -> SimpleEntry {SimpleEntry(date: Date(), configuration: VegetableCategoriesIntent())}func getSnapshot(for configuration: VegetableCategoriesIntent, in context: Context, completion: @escaping @Sendable (SimpleEntry) -> Void) {let simpleEntry = SimpleEntry(date: Date(), configuration: configuration)completion(simpleEntry)}func getTimeline(for configuration: VegetableCategoriesIntent, in context: Context, completion: @escaping @Sendable (Timeline<SimpleEntry>) -> Void) {let vegetableName = vegetableName(for: configuration)let currentDate = Date()let entry = SimpleEntry(date: currentDate, configuration: configuration, vegetableName: vegetableName)let timeline =  Timeline(entries: [entry], policy: .atEnd)completion(timeline)}
}func vegetableName(for configuration: VegetableCategoriesIntent) -> String {switch configuration.vegetable {case .carrot:return "Carrot"case .broccoli:return "Broccoli"case .cucumber:return "Cucumber"case .celery:return "Celery"case .unknown:return "Unknown"}
}struct SimpleEntry: TimelineEntry {let date: Datelet configuration: VegetableCategoriesIntentvar vegetableName: String?
}struct SimpleWidgetEntryView : View {var entry: VegetableCategoriesProvider.Entryvar body: some View {VStack {Text("Time:")Text(entry.date, style: .timer)Text("Favorite vegetable:")Text(entry.vegetableName ?? "未选择")}}
}struct SimpleWidget: Widget {let kind: String = "SimpleWidget"var body: some WidgetConfiguration {IntentConfiguration(kind: kind, intent: VegetableCategoriesIntent.self, provider: VegetableCategoriesProvider()) { entry inSimpleWidgetEntryView(entry: entry).containerBackground(.fill.tertiary, for: .widget)}}
}

顺利的话编译就通过了,上面代码中修改了以下 UI,将选择的 vegetable 显示出来了。
请添加图片描述


3. 动态创建 ConfigurationIntent(以 Country Widget 为例)

首先先看几个概念:

  • Configuration Intent:用于在 Widget 配置界面(或 Shortcuts UI)提供“参数选择”。
  • AppIntents:Apple 在较新 SDK 中提供的替代/补充方案,用于定义可供系统调用的动作与实体。
  • AppEntity:在 AppIntents 中表示一个“实体对象”(如联系人、地点或本例的国家),支持被系统识别、建议与持久化。
  • EntityQuery:为 AppEntity 提供加载/查找/建议项的方法(同步或异步)。
  • parameterSummary:用于在系统 UI 中以简洁的方式展示当前参数的摘要。Summary { \.$country } 会把参数投影(projected)值显示出来。

下面我们创建一个选择国家的 Widget,我们用 CountrySelectIntent(一个 Configuration Intent)作为 Widget 的配置参数,并通过 CountryEntry: AppEntity + CountryEntryQuery: EntityQuery 来提供建议列表与实体解析。Widget 的 TimelineProvider 读取用户选择的实体,渲染对应内容。

3.1 在 Intent 文件中定义实体与参数(CountrySelectIntent.swift

// CountrySelectIntent: 用于 Widget 配置的 Intent 定义
struct CountrySelectIntent: WidgetConfigurationIntent {static var title: LocalizedStringResource = "Select Country"static var description: IntentDescription = "Choose a country to display its information in the widget."// 使用 AppEntity 类型作为参数,可在系统 UI 中显示实体并支持建议列表@Parameter(title: "Country", description: "select a country")var country: CountryEntry// 在 UI 摘要处显示已选的 country(即 CountryEntry 的 displayRepresentation)static var parameterSummary: some ParameterSummary {Summary { \.$country }}
}

关键点说明:

  • @Parameter 的类型是 CountryEntry(实现了 AppEntity),不是普通的 String。这使得系统能展示“实体选择”界面,而不是只能输入文本。
  • parameterSummarySummary { \.$country } 表达式用来告诉系统在 Widget 编辑中如何显示用户当前的选择;\.$country 是参数的投影值(projected value),它会渲染为实体的 displayRepresentation

接着看 CountryEntryCountryEntryQuery

// CountryEntry: 一个轻量的 AppEntity,用来表示“国家”这一实体
struct CountryEntry: AppEntity, Identifiable {// 系统展示类型名称static var typeDisplayRepresentation: TypeDisplayRepresentation {TypeDisplayRepresentation(name: "Country")}// 默认的查询实现(当系统需要列出/解析此实体时会使用它)static var defaultQuery = CountryEntryQuery()// AppEntity 需要一个唯一 id,这里以 countryCode 作为 idvar id: String { countryCode }let countryCode: String// 方便在运行时获取一个默认实体(eg. 用作回退值)static func defaultValue() -> CountryEntry {CountryEntry(countryCode: "US")}// 给调用方暴露一个友好的 namevar countName: String {Self.names[countryCode] ?? countryCode}// 国家代码 -> 名称 映射(可扩展为更多国家或国际化)private static let names: [String: String] = ["US": "United States","CA": "Canada","GB": "United Kingdom","FR": "France","DE": "Germany"]// 用于 AppIntents/UI 的显示;系统会把这个字符串展示在选择/摘要中var displayRepresentation: DisplayRepresentation {let name = Self.names[countryCode] ?? countryCode// 这里使用简单的 stringLiteral 表示:"United States (US)"return DisplayRepresentation(stringLiteral: "\(name) (\(countryCode))")}
}// CountryEntryQuery: 实现 EntityQuery
struct CountryEntryQuery: EntityQuery {typealias EntityType = CountryEntry// 静态的示例数据(可替换为从网络或数据库加载的动态列表)var dataList: [CountryEntry] = [CountryEntry(countryCode: "US"),CountryEntry(countryCode: "CA"),CountryEntry(countryCode: "GB"),CountryEntry(countryCode: "FR"),CountryEntry(countryCode: "DE")]// 系统使用的默认 querystatic var defaultQuery: CountryEntryQuery {return CountryEntryQuery()}// entities(for:): 给定一组标识符,返回对应实体。通常用于反解析已保存的配置。func entities(for identifiers: [String]) async throws -> [CountryEntry] {return identifiers.map { CountryEntry(countryCode: $0) }}// suggestedEntities(): 返回供 UI 显示的建议实体列表(可以是静态或动态的)func suggestedEntities() async throws -> [CountryEntry] {return dataList}
}

说明:

  • CountryEntrycountryCode 作为 id,并实现 displayRepresentation 返回可读文本(系统用于 UI 展示)。
  • CountryEntryQuerysuggestedEntities() 返回系统将用来显示在选择界面的建议项(本示例返回静态列表,但可以改为异步从网络加载)。
  • entities(for:) 用于把标识符数组还原为实体(系统在持久化/恢复配置时会调用)。

3.2 在 Widget 中使用 Configuration Intent

Widget 使用 AppIntentConfiguration(或 IntentConfiguration)来指定 intent 类型与 provider:

struct CountryProvider: AppIntentTimelineProvider {typealias Intent = CountrySelectIntenttypealias Entry = CountryTimelineEntryfunc placeholder(in context: Context) -> CountryTimelineEntry {CountryTimelineEntry(date: Date(), configuration: CountrySelectIntent(), country: CountryEntry(countryCode: "US"))}func snapshot(for configuration: CountrySelectIntent, in context: Context) async -> CountryTimelineEntry {CountryTimelineEntry(date: Date(), configuration: CountrySelectIntent(), country: CountryEntry(countryCode: "US"))}func timeline(for configuration: CountrySelectIntent, in context: Context) async -> Timeline<CountryTimelineEntry> {let currentDate = Date()let entry = CountryTimelineEntry(date: currentDate, configuration: configuration, country: configuration.country)let timeline = Timeline(entries: [entry], policy: .atEnd)return timeline}
}struct CountryTimelineEntry: TimelineEntry {let date: Datelet configuration: CountrySelectIntentlet country: CountryEntry
}struct CountryView: View {var entry: CountryProvider.Entryvar body: some View {VStack {Text("Country:")Text(entry.country.countName).font(.headline)}}
}struct CountryWidget: Widget {let kind: String = "CountryWidget"var body: some WidgetConfiguration {AppIntentConfiguration(kind: kind, intent: CountrySelectIntent.self, provider: CountryProvider()) { entry inCountryView(entry: entry)}.configurationDisplayName("Country Widget").description("Displays information about the selected country.")}
}

关键点:

  • CountryProvider.timeline(for:in:)configuration 参数是 CountrySelectIntent,并且 configuration.countryCountryEntry 实例(由系统生成/反序列化),可以直接用于渲染。
  • placeholder / snapshot 中,建议提供合理值。

使用示例仅仅是为了使用 Intent,并未对数据进行本地缓存以及一些其他的优化。

请添加图片描述


4. 本地化、性能与安全注意事项

  • 本地化:在 .intentdefinition 中为每个 Item 设置本地化的 displayString,或在 Localizable.strings 中提供翻译。
  • 性能:Intents extension 运行时间短,避免同步等待网络请求;若确需网络,使用缓存(App Group)并优先返回缓存结果。
  • 权限与隐私:如果动态选项涉及用户隐私数据(联系人、日历等),在主 App 中请求并获取权限,Intents extension 需谨慎处理权限敏感操作。

5. AppIntents 的简要对比与迁移建议

  • iOS 16 引入了 AppIntents,它使用 Swift 原生 API 编写 Intent,替代了 .intentdefinition 的可视化工作流。
  • 优点:写 Swift 代码更直观,测试更方便。缺点:最低支持 iOS 16。
  • 迁移建议:如果你的应用仅面向 iOS 16+,优先考虑 AppIntentConfiguration;若需兼容 iOS 14/15 或已有大量 .intentdefinition,继续使用 Intents 文件。

6. 总结

  • 使用 IntentConfiguration 可以为 Widget 提供用户可配置的参数选择,适用于需要兼容较旧 iOS 版本或已有 .intentdefinition 的项目。
  • 静态选项简单直接:直接在 .intentdefinition 文件中添加 Items 并生成代码。
  • 动态选项需实现相应的 Intents 协议及回调,注意性能与缓存策略。
  • 推荐实践:把图片资源放在 Widget 的 Assets、把耗时网络操作迁移到 App 并通过 App Group 缓存结果、在 .intentdefinition 中做好本地化。

最后,希望能够帮助到有需要的朋友,如果觉得有帮助,还望点个赞,添加个关注,笔者也会不断地努力,写出更多更好用的文章。

http://www.dtcms.com/a/593866.html

相关文章:

  • 教育网站报名网络推广100种方法免费
  • 班级网站设计论文网站制作与网站建设技术
  • 卓拙科技做网站吗网站建设期末论文
  • 【App开发】Android Studio 安装插件(比如通义灵码)
  • 在线生成个人网站注册建设通网站首页
  • 栖霞建设采购网站用wordpress插件推荐
  • 网站开发前后端语言2017主流网站开发语言
  • 数仓开发中口径发散如何治理?
  • SEO网站建设入驻程流网页游戏排行2020前十名
  • EasyGBS/EasyNVR高并发适配!PostgreSQL部署指南
  • 网站开发公司怎么选择动态交互网站建设
  • 杭州网站的建设企业管理软件的发展趋势
  • git如何回退到指定提交
  • 自己做网站用中文为什么是乱码大型网站开发 优帮云
  • 六爻观测基础(四)——地支
  • 抖音官网链接网站怎么做缪斯设计公司
  • Uniapp ECG心电图组件
  • 高性能抗干扰两线电流型霍尔开关SC25898 | 赛卓电子重磅新品
  • 仁怀哪里有做网站的做效果图常用的网站有哪些
  • 2025 11 09 作业
  • 廊坊网站seo服务深圳市光明区官网
  • 大模型调用完全指南(含免费资源汇总)
  • 定义数组指针
  • 做搜狗手机网站点网站第三方统计工具
  • 专业钓场计时计费管理系统:提升运营效率的智能化解决方案
  • 如何做一个与博物馆相关网站卡板技术支持 东莞网站建设
  • 北大软件外事管理系统:以“制度+技术”,筑牢外事管理 数字化屏障
  • wordpress怎么连接主机名aso如何优化
  • PDF文件内容出现重叠现象解析
  • 织梦系统怎么做网站个人网站备案信息填写