iOS14 Widget小组件开发(Widget Extension)

您所在的位置:网站首页 ios小组 iOS14 Widget小组件开发(Widget Extension)

iOS14 Widget小组件开发(Widget Extension)

2023-07-27 01:33| 来源: 网络整理| 查看: 265

自iOS8之后,苹果支持了扩展(Extension)的开发,开发者可以通过系统提供给我们的扩展接入点 (Extension point) 来为系统特定的服务提供某些附加的功能。 但iOS14后,苹果更新了扩展组件,引入了新的UI组件:WidgetKit 而舍弃了iOS14以下版本的Today Extension组件

开发须知 WidgetExtension 使用的是新的WidgetKit不同于Today Widget,它只能使用 SwiftUI 进行开发,所以需要SwiftUI和Swift基础 Widget只支持3种尺寸systemSmall (2x2)、 systemMedium (4x2)、 systemLarge(4x4) 默认点击Widget打开主应用程序 Widget类似于Today Widget是一个独立运行的程序,需要在项目中进行 App Groups 的设置才能使其与主程序互通数据,这点与Today Widget相同 Apple官方已经弃用Today Extension,Xcode12已经不再提供Today Extension的添加,已经有Today Widget的应用则会显示到一个特定的区域进行展示

Widget官方说明

Widget实现 1.创建添加Widget Extension

File -> New -> Target -> Widget Extension 创建

Include Configuration Intent 如果你所创建的Widget需要支持用户自定义配置属性,则需要勾选这个(例如天气组件,用户可以选择城市;记事本组件,用户记录信息等),不支持的话则不用勾选

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

创建

2.Widget文件函数解析 Provider

为小组件展示提供一切必要信息的结构体,遵守TimelineProvider协议,产生一个时间线,告诉 WidgetKit 何时渲染与刷新 Widget,时间线包含一个你定义的自定义TimelineEntry类型。时间线条目标识了你希望WidgetKit更新Widget内容的日期。在自定义类型中包含你的Widget的视图需要渲染的属性。

struct Provider: TimelineProvider { // 占位视图 func placeholder(in context: Context) -> SimpleEntry { SimpleEntry(date: Date()) } // 编辑屏幕在左上角选择添加Widget、第一次展示时会调用该方法 func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) { let entry = SimpleEntry(date: Date()) completion(entry) } // 进行数据的预处理,转化成Entry func getTimeline(in context: Context, completion: @escaping (Timeline) -> ()) { var entries: [SimpleEntry] = [] // Generate a timeline consisting of five entries an hour apart, starting from the current date. let currentDate = Date() for hourOffset in 0 .. Void) { let url = URL(string: "https://nowapi.navoinfo.cn/get/now/today")! let task = URLSession.shared.dataTask(with: url) { (data, response, error) in guard error==nil else{ completion(.failure(error!)) return } let poster=posterFromJson(fromData: data!) completion(.success(poster)) } task.resume() } static func posterFromJson(fromData data:Data) -> Poster { let json = try! JSONSerialization.jsonObject(with: data, options: []) as! [String: Any] guard let result = json["result"] as? [String: Any] else{ return Poster(author: "Now", content: "加载失败") } let author = result["author"] as! String let content = result["celebrated"] as! String let posterImage = result["poster_image"] as! String //图片同步请求 var image: UIImage? = nil if let imageData = try? Data(contentsOf: URL(string: posterImage)!) { image = UIImage(data: imageData) } return Poster(author: author, content: content, posterImage: image) } } 复制代码

SwiftUI中的Image没有提供直接加载URL方式的图片显示

在getTimeline中进行数据请求中completion(timeline)执行完之后,不再支持图片的异步回调,用异步加载的方式就无法加载网络图片,所以必须在数据请求回来的处理中采用同步方式,将图片的data获取,转换成UIImage,在赋值给Image展示

4).数据加载处理 func getTimeline(in context: Context, completion: @escaping (Timeline) -> ()) { let currentDate = Date() //设定1小时更新一次数据 let updateDate = Calendar.current.date(byAdding: .hour, value: 1, to: currentDate)! PosterData.getTodayPoster { result in let poster: Poster if case .success(let fetchedData) = result{ poster = fetchedData }else{ poster=Poster(author: "Now", content: "Now格言"); } let entry = Entry(date: currentDate, poster: poster) let timeline = Timeline(entries: [entry], policy: .after(updateDate)) completion(timeline) } } 复制代码 5).页面搭建展示 struct NowPosterWidgetEntryView : View { var entry: PosterProvider.Entry var body: some View { ZStack{ Image(uiImage: entry.poster.posterImage!) .resizable() .frame(minWidth: 169, maxWidth: .infinity, minHeight: 169, maxHeight: .infinity, alignment: .center) .scaledToFill() .edgesIgnoringSafeArea(.all) .aspectRatio(contentMode: .fill) Text(entry.poster.content) .foregroundColor(Color.white) .lineLimit(4) .font(.system(size: 14)) .padding(.horizontal) } .widgetURL(URL(string: "跳转链接")) } } 复制代码

然后更新补全placeholder getSnapshot Previews处相应的Entry即完成Widget内容展示

实现展示

5.Widget点击交互

点击Widget窗口唤起APP进行交互指定跳转支持两种方式:

widgetURL:点击区域是Widget的所有区域,适合元素、逻辑简单的小部件 Link:通过Link修饰,允许让界面上不同元素产生点击响应

Widget三种尺寸规格中

systemSmall只能用widgetURL实现URL传递接收 var body: some View { ZStack{ //UI编写 } .widgetURL(URL(string: "跳转链接widgetURL")) } 复制代码 systemMedium、systemLarge可以用Link或者widgetUrl处理 var body: some View { Link(destination: URL(string: "跳转链接Link")!){ VStack{ //UI编写 } } } 复制代码

接收方式 APPDelegate中接收返回的URL

//swift func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { } //OC -(BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options{ if ([url.scheme isEqualToString:@"NowWidget"]){ //执行跳转后的操作 } return YES; } 复制代码

如果项目实现了SceneDelegate 则需要在SceneDelegate里面实现跳转处理

func scene(_ scene: UIScene, openURLContexts URLContexts: Set) { for context in URLContexts { print(context.url) } } 复制代码 数据共享

由于widget跟APP间相互独立,如果想用相同的数据则需要两者间数据共享,创建 App Group 主APP中 Target -> Signing & Capability -> +Capability -> 添加 App Group APPGroups创建.png

ps:网上说的还需创建申请 APPID 但在开启自动管理 Automatically manage signing的情况下xcode会自动给你创建相关联的APPID

两者间的数据共享主要通过UserDefaults 和FileManager 两种形式。 以OC中使用UserDefaults共享数据为例

//存数据 NSUserDefaults *userDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.imoblife.now"]; [userDefaults setObject:@"content" forKey:@"widget"]; [userDefaults synchronize]; //取数据 NSUserDefaults *userDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.imoblife.now"]; NSString *content = [userDefaults objectForKey:@"widget"]; 复制代码

oc、swift混编调用

文件共享及pods共享

文件共享 文件共享.png 勾选共享widget选项即可

pods共享 正常使用下widget中无法使用pods导入的第三方SDK如Masonry等,会造成布局等极其不便,因此需要共享pods,在Podfile中需要另设置并重新install

source 'https://github.com/CocoaPods/Specs.git' platform :ios, '9.0' inhibit_all_warnings! use_frameworks! #共享HandyJSON def share_pods pod 'HandyJSON' end target "targetName" do pod 'Alamofire' share_pods end target "widgetTargetName" do share_pods end 复制代码

完成后即可使用pods中的第三方SDK了

Pods第三方SDK使用错误提示 如果在pods导入共享第三方库,或者使用[UIApplication sharedApplication]方法报错如下时

not available on iOS (App Extension) - Use view controller based solutions where appropriate instead.

则需要在pods Target里面,选中出错的SDK并点击buildSettings 搜索Require 然后把Require Only App-Extension-Safe API 然后把YES改为**NO**即可 修正路径说明.png ps:工程项目里也可按照这个方法去排查原因



【本文地址】


今日新闻


推荐新闻


    CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3