Jun
30
上一话http://lrdcq.com/me/read.php/106.htmWidgetKit是从StaticConfiguration入手的,而避开了实际上应该更常用的IntentConfiguration。而理解在WidgetKit中出现的Intent,显然不能按照Siri中的Intent去理解了。在WidgetKit中,Intent更多的是用来呈现长按配置菜单中的选项,简单的来说,Intent是一个配置表。如下图:
这个LRDConfigurationIntent的内容可以理解为,这个自定义Intent是一个Widget展示用的配置(就是钩上的那个),其中有一个参数,叫Mode,这个Mode的类型是下面的自定义类型LRDMode,并且标明内容是动态生成。LRDMode的定义如下:
除了默认参数,我们新增了一个参数为name。顺带一提,这个场景下intent的response就完全没意义了。
intent用法
以上配置是官方文档说明中描述的最复杂的配置,其中LRDConfigurationIntent的参数其实就是Widget的配置项目了,这里我们的参数是一个自定义的type,不过正常情况下内置类型,如下:
就可以应付绝大部分的场景了。
自定义type也分为固定的和动态生成的。一般来说固定的,也就是在这个配置里直接写明可选的枚举就可以解决了,如上进行动态生成的场景也是少数中的少数。因此我们按照最罕见的场景来尝试。
完成动态intent选项的配置,我们需要新建一个Intents Extension去承接LRDConfigurationIntent的处理。刚才在intent配置里编写的那些东西,Xcode会自动生成代码,主要是
和其对应的LRDConfigurationIntentHandling之类的协议,我们的IntentHandler主要就是实现相关东西。
要提供LRDMode的可选项,主要是实现协议中provide{Mode}OptionsCollection这个方法,这个方法其实就是通过程序,异步丢一堆INObject实例进去作为枚举项目,简单的用法看起来如下:
IntentConfiguration使用
使用IntentConfiguration,和StaticConfiguration相比,其实就是多传了一个Intent进入:
然后提供的LRDProvider不再是实现于TimelineProvider,而是一个类似的东西IntentTimelineProvider。这个协议上的差别是,入参多出了LRDConfigurationIntent本身,因此处理的时候可以直接获取到LRDConfigurationIntent对象进行读取,类似于:
其他
这个调试过程远比之前复杂很多,注意几点:
a. Intent配置文件修改后重新生成相关源码有延迟的,大概率需要重新build一下,类似于安卓修改了gradle脚本后要rebuild一样。千万别忘了导致写错代码。
b. 同样,由于目前整个程序出现了三部分了(主app,widget ex,intents ex),从数据提供的角度,任何修改都必须先run app,再run intents ex,再run widget ex才对,否则还是容易出岔子。
c. 就算用简单配置不存在Intents Extension,Intent配置Widget相关逻辑也不能在模拟器上运行,必须真机调试。给苹果一个大写的服。
d. 苹果为何Widget的用户配置要通过Intent这种蛋疼的方式来实现,可以想象一下:大部分去实现Widget的开发者,都是深度苹果开发者(比如苹果自己的系统应用开发者),他们的应用本身就大量依赖Intent完成大量用户可定制的内容。那么将相关配置继承下来让Widget可以继续使用,也是很自然的了。
学习资料:以上内容还是从学习资料实践而来
https://developer.apple.com/documentation/widgetkit
这个LRDConfigurationIntent的内容可以理解为,这个自定义Intent是一个Widget展示用的配置(就是钩上的那个),其中有一个参数,叫Mode,这个Mode的类型是下面的自定义类型LRDMode,并且标明内容是动态生成。LRDMode的定义如下:
除了默认参数,我们新增了一个参数为name。顺带一提,这个场景下intent的response就完全没意义了。
intent用法
以上配置是官方文档说明中描述的最复杂的配置,其中LRDConfigurationIntent的参数其实就是Widget的配置项目了,这里我们的参数是一个自定义的type,不过正常情况下内置类型,如下:
就可以应付绝大部分的场景了。
自定义type也分为固定的和动态生成的。一般来说固定的,也就是在这个配置里直接写明可选的枚举就可以解决了,如上进行动态生成的场景也是少数中的少数。因此我们按照最罕见的场景来尝试。
完成动态intent选项的配置,我们需要新建一个Intents Extension去承接LRDConfigurationIntent的处理。刚才在intent配置里编写的那些东西,Xcode会自动生成代码,主要是
@objc(LRDConfigurationIntent)
public class LRDConfigurationIntent: INIntent {
@NSManaged public var Mode: LRDMode?
}
@objc(LRDMode)
public class LRDMode: INObject {
@available(iOS 13.0, macOS 10.16, watchOS 6.0, *)
@NSManaged public var name: String?
}
和其对应的LRDConfigurationIntentHandling之类的协议,我们的IntentHandler主要就是实现相关东西。
要提供LRDMode的可选项,主要是实现协议中provide{Mode}OptionsCollection这个方法,这个方法其实就是通过程序,异步丢一堆INObject实例进去作为枚举项目,简单的用法看起来如下:
func provideModeOptionsCollection(for intent: LRDConfigurationIntent, with completion: @escaping (INObjectCollection<LRDMode>?, Error?) -> Void) {
//直接构成三个数据
let mode_a = LRDMode(identifier:"mode_a", display: "mode_a");
mode_a.name = "a模式"//自定义添加的属性不会自动生成构造器,比较麻烦啦
let mode_b = LRDMode(identifier:"mode_b", display: "mode_b");
mode_b.name = "b模式"
let mode_c = LRDMode(identifier:"mode_c", display: "mode_c");
mode_c.name = "c模式"
//构成INObjectCollection,completion出去
let collection = INObjectCollection(items: [mode_a, mode_b, mode_c])
completion(collection, nil)
}
IntentConfiguration使用
使用IntentConfiguration,和StaticConfiguration相比,其实就是多传了一个Intent进入:
struct LRDWidget: Widget {
public var body: some WidgetConfiguration {
IntentConfiguration(
kind: "key",
intent: LRDConfigurationIntent.self,//这个
provider: LRDProvider(),
placeholder: LRDPlaceholderView()
) { entry in
LRDEntryView(entry: entry)
}
.configurationDisplayName("configurationDisplayName")
.description("descriptions")
}
}
然后提供的LRDProvider不再是实现于TimelineProvider,而是一个类似的东西IntentTimelineProvider。这个协议上的差别是,入参多出了LRDConfigurationIntent本身,因此处理的时候可以直接获取到LRDConfigurationIntent对象进行读取,类似于:
func timeline(for configuration: LRDConfigurationIntent, with context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
let name = configuration.Mode?.name ?? "";
//then do something....
}
其他
这个调试过程远比之前复杂很多,注意几点:
a. Intent配置文件修改后重新生成相关源码有延迟的,大概率需要重新build一下,类似于安卓修改了gradle脚本后要rebuild一样。千万别忘了导致写错代码。
b. 同样,由于目前整个程序出现了三部分了(主app,widget ex,intents ex),从数据提供的角度,任何修改都必须先run app,再run intents ex,再run widget ex才对,否则还是容易出岔子。
c. 就算用简单配置不存在Intents Extension,Intent配置Widget相关逻辑也不能在模拟器上运行,必须真机调试。给苹果一个大写的服。
d. 苹果为何Widget的用户配置要通过Intent这种蛋疼的方式来实现,可以想象一下:大部分去实现Widget的开发者,都是深度苹果开发者(比如苹果自己的系统应用开发者),他们的应用本身就大量依赖Intent完成大量用户可定制的内容。那么将相关配置继承下来让Widget可以继续使用,也是很自然的了。
学习资料:以上内容还是从学习资料实践而来
https://developer.apple.com/documentation/widgetkit