欢迎来真孝善网,为您提供真孝善正能量书籍故事!

深入探索iOS14 Widget小组件开发(Widget Extension)

时间:10-29 神话故事 提交错误

开发须知

WidgetExtension 使用新的WidgetKit,与Today Widget 不同。只能使用SwiftUI进行开发,所以SwiftUI 和Swift basic Widget 仅支持3 种尺寸:systemSmall(2x2)、systemMedium(4x2)、systemLarge(4x4) 默认情况下,点击Widget 打开主要应用。该小部件类似于“今日小部件”。它是一个独立运行的程序。项目中需要设置App Groups,使其能够与主程序进行通信。这与“今日小部件”相同。 Apple 已正式宣布弃用Today 延期。 Xcode12不再提供Today Extension的添加。已经具有Today Widget 的应用程序将显示在特定区域。

Widget官方描述

Widget实现

1.创建添加Widget Extension

File-New-Target-Widget Extension

创建包含配置意图

如果你创建的Widget需要支持用户自定义配置属性,则需要勾选此项(例如天气组件,用户可以选择城市;记事本组件,用户记录信息等),如果支持不支持,无需检查。

本文主要以未勾选用户配置属性的情况说明创建

2.Widget文件函数解析

Provider

一个结构体,提供widget 显示所需的所有信息,遵守TimelineProvider 协议,生成时间线,并告诉WidgetKit 何时渲染和刷新Widget。时间线包含您定义的自定义TimelineEntry。类型。时间线目标标识您希望WidgetKit 更新小部件内容的日期。在自定义类型中包含您的Widget 视图需要呈现的属性。

结构Provider: TimelineProvider {

//占位符视图

func 占位符(在context: 上下文中)-SimpleEntry {

SimpleEntry(date: 日期())

}

//在编辑屏幕的左上角选择添加小部件。第一次显示时会调用该方法。

func getSnapshot(在context: 上下文中,completion: @escaping (SimpleEntry) -()) {

让条目=SimpleEntry(date: Date())

完成(输入)

}

//预处理数据并转换为Entry

func getTimeline(在context: 上下文中,completion: @escaping (时间轴) -()) {

var Entry: [SimpleEntry]=[]

//生成一个时间线,其中包含从当前日期开始的每小时五个条目。

让当前日期=日期()

对于0 . 5 中的hourOffset {

让entryDate=Calendar.current.date(byAdding:hour, value: hourOffset, to: currentDate)!

让条目=SimpleEntry(date: 条目日期)

条目.追加(条目)

}

让时间线=时间线(entries:条目,policy:atEnd)

完成(时间表)

}

}placeholder:提供默认视图。例如,如果网络请求失败,发生未知错误,或者第一次显示widget,就会显示此视图。getSnapshot:为了显示widget库中的widget,WidgetKit需要提供者提供预览Snapshot,可以在组件的添加页面看到效果getTimeline:在该方法中,可以制作一个网络请求,并将获取到的数据保存在对应的表项中。调用完成后,您将获得刷新小部件参数policy:刷新机会

.never:不刷新

.atEnd:显示时间线中最后一个条目后自动刷新。 Timeline方法将被再次调用

.after(date):达到一定时间后自动刷新!!!Widget。刷新时间由系统决定。如果您需要强制刷新Widget,可以使用App 中的WidgetCenter 重新加载所有时间线:WidgetCenter.shared.reloadAllTimelines()010 -59000 TimelineProvider 官方说明

Entry

Timeline的刷新策略是会延迟的,并不一定根据你设定的时间精确刷新。同时官方说明了每个widget窗口小部件每天接收的刷新都会有数量限制需要渲染Widget,需要遵守TimelineEntry协议。

结构SimpleEntry: TimelineEntry {

让date: 日期

}

EntryView

对于不同尺寸的Widget,可以将Widget在屏幕上显示的内容设置为不同的视图。

结构NowWidgetEntryView : 视图{

var Entry: Provider.Entry

//为不同大小的Widget设置不同的View

@Environment(.widgetFamily) var family //环境变量大小

@ViewBuilder

var body: 一些视图{

开关系列{

案例.systemSmall:

//小尺寸

文本(条目.日期,style:时间)

案例.systemMedium:

//中等大小

默认:

//大尺寸

}

}

}

@main 主入口

@main

结构NowWidget: 小部件{

让kind: 字符串="NowWidget"

var body: 一些WidgetConfiguration {

StaticConfiguration(kind: kind,provider: Provider()) { 条目

NowWidgetEntryView(条目:条目)

}

.configurationDisplayName("我的小部件")

.description("这是一个示例小部件。")

//.supportedFamilies([.systemSmall,systemMedium])

}

}数据模型:代表Widget的主入口。系统从这里加载,可以用来实现多个Widget。 Kind:Widget的唯一标识符。@main:初始化配置代码StaticConfiguration:可以自行解析,无需用户任何输入。可以在Widget的App中获取相关数据并发送到WidgetIntentConfiguration:WidgetConfiguration,这取决于App的Siri Intent。这些Intents将被自动接收并用于更新Widget,用于构建动态Widget配置。 DisplayName:添加编辑界面的标题描述显示:添加编辑界面显示的描述内容supportedFamilies:设置widget支持的widget的大小。如果未设置,则默认实现所有三种样式

Widget控件尺寸大小

屏幕尺寸(纵向) Small widgetMedium widgetLarge widget414x896 pt169x169 pt360x169 pt360x376 pt375x812 pt155x155 pt32 9x155 pt329x345 pt414x736 pt159x159 pt348x159 pt348x357 pt375x667 pt148x148 pt322x148 pt322x324 pt320x568 pt141x141 pt291x141 pt291x299 pt

3.多Widget组件实现

小部件只能实现大、中、小三种不同尺寸的组件形式。如果现有的需求是做不同的功能、相同大小的组件,就需要实现多个组件。

1.通过修改原有Widget入口文件方法,添加更多配置支持多个Widget

@主要的

结构NowSwiftWidget: WidgetBundle {

@WidgetBundleBuilder

var body: 一些小部件{

NowPosterWidget()

NowRecommendWidget()

NowDailyWidget()

}

}2.创建另一个SwiftUI文件来实现组件功能,去掉@main,并修改相同的函数名

结构NowPosterWidget: 小部件{

私人让kind: 字符串="NowPosterWidget"

公共var body: 一些WidgetConfiguration {

StaticConfiguration(kind: kind,provider: PosterProvider()) { 条目

NowPosterWidgetEntryView(条目:条目)

}

.configurationDisplayName("座右铭")

.description("现在冥想每日海报,每日签名小部件")

.supportedFamilies([.systemSmall,systemMedium])

}

}

4.Widget数据请求及网络图片加载

1).新建一个swift文件,用于单独处理数据

2).创建网络请求的数据模型

结构海报{

让author:字符串

让content: 字符串

var posterImage: UIImage?=UIImage(named: "plan_collect")

并在Widget页面的Entry中绑定对应的model

结构PosterEntry: TimelineEntry {

让date: 日期

让海报: 海报

}

3).创建请求函数,并且回调请求参数,声明一个请求工具,实现数据请求并将网络图片同步请求

如果主APP是用swift写的,可以共享网络请求模块文件或者pods库(方法后面会介绍)

PosterFromJson 这种数据模型转换方式只适用于简单的接口(为了偷懒)。复杂的数据模型仍应使用HandyJSON 或KaKaJson 进行解析。

如果使用第三方模型转换方式,可以将图片的同步请求处理放在getTodayPoster的请求中,同步处理。

结构海报数据{

静态func getTodayPoster(completion: @escaping (Result) -Void) {

让url=URL(string: "https://nowapi.navoinfo.cn/get/now/today")!

让任务=URLSession.shared.dataTask(with: url) { (数据、响应、错误) in

守卫错误==nil else{

完成(.失败(错误!))

返回

}

让海报=海报FromJson(来自Data:数据!)

完成(.成功(海报))

}

任务.resume()

}

静态函数posterFromJson(fromData data:Data) -海报{

让json=尝试! JSONSerialization.jsonObject(with: data, options: []) as! [String: 任何]

守卫让结果=json["结果"] 作为? [String: 任意] 其他{

return Poster(author: "现在", content: "加载失败")

}

让作者=结果["作者"]为!细绳

让内容=结果["庆祝"]为!细绳

让posterImage=结果["poster_image"] 为!细绳

//图像同步请求

var image: UIImage?=零

如果让imageData=尝试一下?数据(contentsOf: URL(string:海报图像)!){

图像=UIImage(data: imageData)

}

返回海报(author: 作者,content: 内容,posterImage: 图片)

}

}SwiftUI 中的图像不提供直接加载URL 来显示图像。

getTimeline中数据请求完成(时间线)执行后,不再支持图片的异步回调。网络图像无法使用异步加载的方式加载,因此在数据请求的处理中必须使用主要针对于具有用户可配置属性的Widget来返回图像。获取数据,转换为UIImage,赋值给Image进行显示。

4).数据加载处理

func getTimeline(在context: 上下文中,completion: @escaping (时间轴) -()) {

让当前日期=日期()

//设置每小时更新一次数据

让updateDate=Calendar.current.date(byAdding:hour, value: 1, to: currentDate)!

PosterData.getTodayPoster { 结果

让海报: 海报

if case .success(let fetchedData)=结果{

海报=获取数据

}别的{

poster=海报(author: "现在", content: "现在座右铭");

}

让条目=条目(日期:当前日期,海报:海报)

让时间线=时间线(entries: [条目],policy:after(updateDate))

完成(时间表)

}

}

5).页面搭建展示

结构NowPosterWidgetEntryView : 视图{

var Entry: PosterProvider.Entry

var body: 一些视图{

ZStack{

图片(uiImage:条目.海报.海报图像!)

.resizing()

.frame(minWidth: 169,maxWidth:infinity,minHeight: 169,maxHeight:infinity,alignment:center)

.scaledToFill()

.edgesIgnoringSafeArea(.all)

.aspectRatio(contentMode:fill)

文字(参赛作品.海报.内容)

.foregroundColor(颜色.白色)

.lineLimit(4)

.font(.system(size: 14))

.padding(.水平)

}

.widgetURL(URL(string: "跳转链接"))

}

}然后在placeholdergetSnapshotPreviews处更新并完成对应的同步方式,完成Widget内容展示。

实现展示

5.Widget点击交互

点击Widget窗口,调用APP进行交互并指定跳转。支持两种方法:

Entry:点击区域为Widget的所有区域,适合逻辑简单的元素和Widget。widgetURL:通过修改Link,允许界面上的不同元素产生点击响应。 Widget的三种尺寸中

Link只能用于实现URL传递和接收var body: some View {

ZStack{

//UI编写

}

.widgetURL(URL(string: "跳转链接widgetURL"))

}systemSmall,widgetURL可以使用systemMediumsystemLarge来处理var body: some View {

Link(destination: URL(string: "跳转链接Link")!){

虚拟堆栈{

//UI编写

}

}

}Link在APPDelegate中接收返回的URL

//迅速

func application(_ app: UIApplication, 打开url: URL, options: [UIApplication.OpenURLOptionsKey : Any]=[:]) -Bool {

}

//超频

-(BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary*)options{

if ([url.scheme isEqualToString:@"NowWidget"]){

//跳转后执行操作

}

返回是;

}如果项目实现了SceneDelegate,则需要在SceneDelegate中实现跳转处理。

func scene(_ scene: UIScene, openURLContexts URLContexts: 设置) {

对于URLContexts 中的上下文{

打印(上下文.url)

}

}

数据共享

由于widget和APP是相互独立的,如果要使用相同的数据,就需要两者之间共享数据。创建widgetUrl目标-签名能力-+能力-在主APP中添加应用组。

APPGroupscreation.pngps:网上说需要创建应用程序APPID,但是当你开启自动管理签名时,xcode会自动为你创建关联的APPID。

接收方式以OC中使用UserDefaults共享数据为例

//保存数据

NSUserDefaults *userDefaults=[[NSUserDefaults alloc] initWithSuiteName:@"group.com.imoblife.now"];

[userDefaults setObject:@"content" forKey:@"widget"];

[用户默认同步];

//获取数据

NSUserDefaults *userDefaults=[[NSUserDefaults alloc] initWithSuiteName:@"group.com.imoblife.now"];

NSString *content=[userDefaults objectForKey:@"widget"];oc,swift混合调用

文件共享及pods共享

App Group文件共享.png

只需检查共享小部件选项即可。两者间的数据共享主要通过UserDefaults和FileManager两种形式。正常使用情况下,Pod导入的第三方SDK如Masonry无法在widget中使用,会导致布局极其不方便等,因此需要共享pod,需要在widget中设置并重新安装Podfile。

来源"https://github.com/CocoaPods/Specs.git"

平台:ios,“9.0”

禁止所有警告!

使用框架!

#ShareHandyJSON

def share_pods

pod "HandyJSON"

结尾

目标"targetName" 执行

吊舱“阿拉莫菲尔”

共享豆荚

结尾

目标“widgetTargetName”执行

共享豆荚

end完成后,就可以在pod中使用第三方SDK了。

文件共享如果在pod中导入共享的第三方库或者使用【UIApplication sharedApplication】方法,会报如下错误:

在iOS(应用程序扩展)上不可用- 在适当的情况下使用基于视图控制器的解决方案。

需要在pods Target中选择错误的SDK,点击buildSettings搜索Require

然后更改Require Only App-Extension-Safe API 并将YES 更改为pods共享

用户评论

莫阑珊

我一直在找想学习如何制作Apple Watch的小组件教程,这篇文章正好合适!

    有15位网友表示赞同!

高冷低能儿

终于看到了关于 iOS 14 Widget 的详细介绍,感觉很有趣!

    有19位网友表示赞同!

古巷青灯

以前都不知道原来可以自己开发小组件呢! 想试一试看看我能做出来啥。

    有19位网友表示赞同!

灬一抹丶苍白

我的手机现在 Widgets 用的不多,以后也许我会自己开发一些实用的小组件!

    有10位网友表示赞同!

小清晰的声音

"Widget Extension" 听起来很专业,不过还是希望能更详细地解说一下。

    有6位网友表示赞同!

糖果控

希望这篇文章能够提供一些实用的代码示例,让我可以更快上手!

    有5位网友表示赞同!

我没有爱人i

现在很多APP都有他们的小组件,感觉都蛮方便的!

    有6位网友表示赞同!

病态的妖孽

不知道开发的小组件会不会占用手机的内存?

    有10位网友表示赞同!

命里缺他

是不是所有 iPhone 12 都支持 Widgets 呢?

    有9位网友表示赞同!

一纸愁肠。

看了标题,感觉这篇文章挺不错的,希望作者能写得详细易懂!

    有10位网友表示赞同!

凉城°

iOS 14 的 Widget 功能真的很棒!可以根据自己的需求定制化设置。

    有16位网友表示赞同!

走过海棠暮

开发小组件需要什么编程语言呢?

    有17位网友表示赞同!

还未走i

以前就看过别人的手机 Widgets,还挺想自己开发一下看看。

    有6位网友表示赞同!

鹿先森,教魔方

这篇文章应该能帮到我想学习 iOS 开发的朋友们!

    有16位网友表示赞同!

无所谓

Widget 的设计 really cool! 希望这篇教程能够详细讲解怎么制作。

    有7位网友表示赞同!

孤独症

真的期待看到更多酷炫的小组件作品!

    有14位网友表示赞同!

◆残留德花瓣

希望这篇文章能够解释一下 Widget 在后台运行的时候占用多少资源?

    有7位网友表示赞同!

花容月貌

我想开发一个可以显示我的日历和待办事项的小组件!

    有20位网友表示赞同!

堕落爱人!

这篇文章一定可以让我想学习编程的朋友们眼前一亮!

    有15位网友表示赞同!

有一种中毒叫上瘾成咆哮i

现在手机上好多APP的 Widgets 都挺好用的,期待了解更多新功能的加入!

    有9位网友表示赞同!

【深入探索iOS14 Widget小组件开发(Widget Extension)】相关文章:

1.蛤蟆讨媳妇【哈尼族民间故事】

2.米颠拜石

3.王羲之临池学书

4.清代敢于创新的“浓墨宰相”——刘墉

5.“巧取豪夺”的由来--米芾逸事

6.荒唐洁癖 惜砚如身(米芾逸事)

7.拜石为兄--米芾逸事

8.郑板桥轶事十则

9.王献之被公主抢亲后的悲惨人生

10.史上真实张三丰:在棺材中竟神奇复活