iOS桌面小插件 Widget Extension
iOS桌面小插件 Widget Extension
- 这个插件时iOS14以后才出现的,基于SwiftUI

- 旧项目新建时可能一堆错误,其中一个时要把插件target 开发sdk版本设置为14.0以上

新建target
- File - Target - Widget Extension

项目结构
- @main 这里是主入口,这里可以设置小组件的 Provider以及 WidgetEntryView,以及长按后弹出框的 APP 信息设置。
- Provider:控制器,这里可以用来做小组件的刷新操作
- SimpleEntry: 这个是数据模型,Provider 里如果想更新数据到 WidgetEntryView,必须通过 SimpleEntry 来实现,当然命名随意了,但是这个必须继承 TimelineEntry。同时也可以新增参数,变量什么的,用来传递自己需要的数据类型。
- WidgetEntryView: 这就是主视图了,在这里自定义页面用来显示在手机桌面。
import WidgetKit
import SwiftUI
import Intents
// 控制器,类似Controller,这里可以用来做小组件的刷新操作
struct Provider: IntentTimelineProvider {
func placeholder(in context: Context) -> SimpleEntry {
SimpleEntry(date: Date(), configuration: ConfigurationIntent())
}
func getSnapshot(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (SimpleEntry) -> ()) {
let entry = SimpleEntry(date: Date(), configuration: configuration)
completion(entry)
}
func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
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 ..< 5 {
let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)!
let entry = SimpleEntry(date: entryDate, configuration: configuration)
entries.append(entry)
}
let timeline = Timeline(entries: entries, policy: .atEnd)
completion(timeline)
}
}
// 数据模型,数据显示在View上必须经过这里
struct SimpleEntry: TimelineEntry {
let date: Date
let configuration: ConfigurationIntent
}
// View,小组件的界面
struct WidgetExtensionEntryView : View {
var entry: Provider.Entry
var body: some View {
Text(entry.date, style: .time)
}
}
// 程序入口,初始化相关信息,如Provider,View等
@main
struct WidgetExtension: Widget {
let kind: String = "WidgetExtension"
var body: some WidgetConfiguration {
IntentConfiguration(kind: kind, intent: ConfigurationIntent.self, provider: Provider()) { entry in
WidgetExtensionEntryView(entry: entry)
}
.configurationDisplayName("小组件")
.description("This is an 测试一下 widget.")
}
}
// 自定义样式
struct WidgetExtension_Previews: PreviewProvider {
static var previews: some View {
// 设置小组件尺寸 systemSmall systemMedium systemLarge
WidgetExtensionEntryView(entry: SimpleEntry(date: Date(), configuration: ConfigurationIntent()))
.previewContext(WidgetPreviewContext(family: .systemSmall))
}
}
自定义UI
自定义小组件尺寸
// 自定义样式
struct WidgetExtension_Previews: PreviewProvider {
static var previews: some View {
// 设置小组件尺寸 systemSmall systemMedium systemLarge
WidgetExtensionEntryView(entry: SimpleEntry(date: Date(), configuration: ConfigurationIntent()))
.previewContext(WidgetPreviewContext(family: .systemSmall))
}
}
HStack、VStack、ZStack
- HStack、VStack相当于UIStackView,H是水平方向,V是竖直方向。ZStack可以理解为相对于屏幕里外方向,也就是相当于以前superView和subView的方式。
// View,小组件的界面
struct WidgetExtensionEntryView : View {
var entry: Provider.Entry
var body: some View {
// 深度布局,屏幕深度
ZStack(alignment: .center, content: {
// 背景图
Image("2").resizable().aspectRatio(contentMode: .fit)
// 水平
HStack(alignment: .center, spacing: 5, content: {
// 左侧图
Image("1").frame(width: 80, height: 80, alignment: .center).aspectRatio(contentMode: .fit).cornerRadius(10.0)
// 垂直
VStack(alignment: .center, spacing: 5, content: {
// 右侧文字
Text("小组件1").foregroundColor(.blue)
Text("小组件2").foregroundColor(.blue).lineLimit(2)
})
})
})
}
}
传递数据
- 通过widgetURL 和Link
- 在主应用添加 URL Types
// View,小组件的界面
struct WidgetExtensionEntryView : View {
var entry: Provider.Entry
var body: some View {
// 深度布局,屏幕深度
ZStack(alignment: .center, content: {
// 背景图
Image("2").resizable().aspectRatio(contentMode: .fit)
// 水平
HStack(alignment: .center, spacing: 5, content: {
// 左侧图
Image("1").frame(width: 80, height: 80, alignment: .center).aspectRatio(contentMode: .fit).cornerRadius(10.0)
// 垂直
VStack(alignment: .center, spacing: 5, content: {
// 右侧文字
Text("小组件1").foregroundColor(.blue)
Text("小组件2").foregroundColor(.blue).lineLimit(2)
})
})
}).widgetURL(URL(string: "widgetExtensionDemo://test1"))
}
}
接受数据
只能用SceneDelegate来接受数据,AppDelegate不行。
SceneDelegate中相应事件
- (void)scene:(UIScene *)scene openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts{
NSLog(@"%s",__FUNCTION__);
UIOpenURLContext * context = URLContexts.allObjects.firstObject;
NSLog(@"%@", context.URL);
}
适配不同尺寸小组件


// View,小组件的界面
struct WidgetExtensionEntryView : View {
@Environment(\.widgetFamily) var family:WidgetFamily
var entry: Provider.Entry
var body: some View {
switch family {
case .systemSmall:
// 深度布局,屏幕深度
ZStack(alignment: .center, content: {
// 背景图
Image("2").resizable().aspectRatio(contentMode: .fill)
// 水平
HStack(alignment: .center, spacing: 5, content: {
// 左侧图
Image("1").frame(width: 80, height: 80, alignment: .center).aspectRatio(contentMode: .fit).cornerRadius(10.0)
// 垂直
VStack(alignment: .center, spacing: 5, content: {
// 右侧文字
Text("小组件1").foregroundColor(.blue)
Text("小组件2").foregroundColor(.blue).lineLimit(2)
})
})
}).widgetURL(URL(string: "widgetExtensionDemo://test1"))
case .systemMedium:
// 深度布局,屏幕深度
ZStack(alignment: .center, content: {
// 背景图
Image("2").resizable().aspectRatio(contentMode: .fill)
// 水平
HStack(alignment: .top, spacing: 5, content: {
// 左侧图
Image("1").resizable().aspectRatio(contentMode: .fit).frame(width: 200, height: 80, alignment: .leading).cornerRadius(10.0).foregroundColor(.blue)
// 垂直
VStack(alignment: .trailing, spacing: 5, content: {
// 右侧文字
Text("zh组件1").foregroundColor(.blue)
Text("小组件2").foregroundColor(.blue).lineLimit(2)
}).foregroundColor(.gray)
})
}).widgetURL(URL(string: "widgetExtensionDemo://test2"))
case .systemLarge:
// 深度布局,屏幕深度
ZStack(alignment: .center, content: {
// 背景图
Image("2").resizable().aspectRatio(contentMode: .fill)
// 水平
HStack(alignment: .center, spacing: 5, content: {
// 左侧图
Image("1").aspectRatio(contentMode: .fit).cornerRadius(10.0).frame(width: 200, height: 100, alignment: .leading)
// 垂直
VStack(alignment: .center, spacing: 5, content: {
// 右侧文字
Text("小组件1").foregroundColor(.blue)
Text("小组件2").foregroundColor(.blue).lineLimit(2)
})
}).foregroundColor(.blue)
}).widgetURL(URL(string: "widgetExtensionDemo://test3"))
default:
// 深度布局,屏幕深度
ZStack(alignment: .center, content: {
// 背景图
Image("2").resizable().aspectRatio(contentMode: .fit)
// 水平
HStack(alignment: .center, spacing: 5, content: {
// 左侧图
Image("1").frame(width: 80, height: 80, alignment: .center).aspectRatio(contentMode: .fit).cornerRadius(10.0)
// 垂直
VStack(alignment: .center, spacing: 5, content: {
// 右侧文字
Text("小组件1").foregroundColor(.blue)
Text("小组件2").foregroundColor(.blue).lineLimit(2)
})
})
}).widgetURL(URL(string: "widgetExtensionDemo://test1"))
}
}
}
更多小组件创建
- 重写@main入口
// 更多小组件
@main
struct Widgets:WidgetBundle {
init() {
}
@WidgetBundleBuilder
var body: some Widget{ // 最多创建5次,也就是15个小组件
WidgetExtension()
CustomWidget()
CustomWidget()
CustomWidget()
CustomWidget()
}
}
struct CustomWidget:Widget {
var kind:String="自定义组件"
var body: some WidgetConfiguration{
IntentConfiguration(kind: kind, intent: ConfigurationIntent.self, provider: Provider()) { entry in
CustomEntryView(entry:entry)
}
.configurationDisplayName("自定义更多组件")
.description("ios14自定义更多小组件")
}
}
// 自定义Ui
struct CustomEntryView:View {
@Environment(\.widgetFamily) var family:WidgetFamily
var entry: Provider.Entry
@ViewBuilder
var body: some View {
switch family {
case .systemSmall:
// 深度布局,屏幕深度
ZStack(alignment: .center, content: {
// 背景图
Image("2").resizable().aspectRatio(contentMode: .fill)
// 水平
HStack(alignment: .center, spacing: 5, content: {
// 左侧图
Image("1").frame(width: 80, height: 80, alignment: .center).aspectRatio(contentMode: .fit).cornerRadius(10.0)
// 垂直
VStack(alignment: .center, spacing: 5, content: {
// 右侧文字
Text("小组件1").foregroundColor(.blue)
Text("小组件2").foregroundColor(.blue).lineLimit(2)
})
})
}).widgetURL(URL(string: "widgetExtensionDemo://test1"))
case .systemMedium:
// 深度布局,屏幕深度
ZStack(alignment: .center, content: {
// 背景图
Image("2").resizable().aspectRatio(contentMode: .fill)
// 水平
HStack(alignment: .top, spacing: 5, content: {
// 左侧图
Image("1").resizable().aspectRatio(contentMode: .fit).frame(width: 200, height: 80, alignment: .leading).cornerRadius(10.0).foregroundColor(.blue)
// 垂直
VStack(alignment: .trailing, spacing: 5, content: {
// 右侧文字
Text("zh组件1").foregroundColor(.blue)
Text("小组件2").foregroundColor(.blue).lineLimit(2)
}).foregroundColor(.gray)
})
}).widgetURL(URL(string: "widgetExtensionDemo://test2"))
case .systemLarge:
// 深度布局,屏幕深度
ZStack(alignment: .center, content: {
// 背景图
Image("2").resizable().aspectRatio(contentMode: .fill)
// 水平
HStack(alignment: .center, spacing: 5, content: {
// 左侧图
Image("1").aspectRatio(contentMode: .fit).cornerRadius(10.0).frame(width: 200, height: 100, alignment: .leading)
// 垂直
VStack(alignment: .center, spacing: 5, content: {
// 右侧文字
Text("小组件1").foregroundColor(.blue)
Text("小组件2").foregroundColor(.blue).lineLimit(2)
})
}).foregroundColor(.blue)
}).widgetURL(URL(string: "widgetExtensionDemo://test3"))
default:
// 深度布局,屏幕深度
ZStack(alignment: .center, content: {
// 背景图
Image("2").resizable().aspectRatio(contentMode: .fit)
// 水平
HStack(alignment: .center, spacing: 5, content: {
// 左侧图
Image("1").frame(width: 80, height: 80, alignment: .center).aspectRatio(contentMode: .fit).cornerRadius(10.0)
// 垂直
VStack(alignment: .center, spacing: 5, content: {
// 右侧文字
Text("小组件1").foregroundColor(.blue)
Text("小组件2").foregroundColor(.blue).lineLimit(2)
})
})
}).widgetURL(URL(string: "widgetExtensionDemo://test1"))
}
}
}
iOS桌面小插件 Widget Extension的更多相关文章
- Android桌面小插件——Widget
Android桌面小插件--Widget 效果图 实现 1. 创建Widget类 创建一个Widget类,并实现页面创建的时候,就实现显示时间 package com.kongqw.kqwwidget ...
- Android-Widget桌面小组件
1, 掌握Widget的用:Widget的用途,能够添加到手机桌面的程序 2, Widget的特点和用法步骤: 特点:快捷,方便,个性化,可自定义功能,可及时控制更新Widget显示内容 3, 用法步 ...
- AppWidgetProvider 桌面插件 Widget 广播 MD
Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...
- 【转】【iOS测试系列】常用测试小插件的使用
背景介绍 由于iOS系统的限制,在非越狱的自动化测试中无法实现一些常用的功能,比如不同应用之间来回切换.模拟全局的点击事件等等.但是在越狱的环境下,这些限制就不存在了,我们可以利用各种小插件来实现我们 ...
- 闲聊select和input常用的小插件
前言 在pc端的项目中,经常会用到表单标签,莫过于是select和input这两种,这两种相当常用.但往往原生的功能不尽人意,即使 input中type有n多属性,甚至连时间控件都有,但仍旧满足不了我 ...
- 【Bootstrap】优秀小插件收集
Bootstrap中不乏很多优秀的小插件来让界面更加漂亮.比如之前做过笔记的bootstrap-fileinput,select2,datetimepicker等都是属于这一系列的.这些相对而言比较大 ...
- Android 桌面小部件
1. 添加AppWidgetProvider 实际上就是个带有界面的BroadcastReceiver public class SimpleWidgetProvider extends AppWid ...
- Android开发中实现桌面小部件
详细信息请参考原文:Android开发中实现桌面小部件 在Android开发中,有时候我们的App设计的功能比较多的时候,需要根据需要更简洁的为用户提供清晰已用的某些功能的时候,用桌面小部件就是一个很 ...
- 桌面小部件AppWidgetProvider简单分析
1.一般桌面小部件涉及到的类 AppWidgetProvider :BroadcastRecevier子类,用于接收更新,删除通知 AppWidgetProvderInfo:AppWidget相关信息 ...
随机推荐
- Java多线程专题6: Queue和List
合集目录 Java多线程专题6: Queue和List CopyOnWriteArrayList 如何通过写时拷贝实现并发安全的 List? CopyOnWrite(COW), 是计算机程序设计领域中 ...
- 浅谈FFT(快速傅里叶变换)
前言 啊摸鱼真爽哈哈哈哈哈哈 这个假期努力多更几篇( 理解本算法需对一些< 常 用 >数学概念比较清楚,如复数.虚数.三角函数等(不会的自己查去(其实就是懒得写了(¬︿̫̿¬☆) 整理了一 ...
- .NET Core分析程序集最优美的方法,不用Assembly.LoadFile(),超越ReflectionOnlyLoad
在编写.NET程序的时候,如果需要对一个程序集文件进行分析,我们可以使用Assembly.LoadFile()来加载这个程序集,然后对LoadFile()方法返回的Assembly对象进行进一步的分析 ...
- Java基础(十一)——反射
一.概述 1.介绍 Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法. 加载完类 ...
- Spring Boot配置多个DataSource (转)
使用Spring Boot时,默认情况下,配置DataSource非常容易.Spring Boot会自动为我们配置好一个DataSource. 如果在application.yml中指定了spring ...
- php表单初始化
转载请注明来源:https://www.cnblogs.com/hookjc/ //初始化表单值的函数function InitForm($row,$form="form1"){ ...
- DLL链接库
转载请注明来源:https://www.cnblogs.com/hookjc/ 2. 静态链接库 对静态链接库的讲解不是本文的重点,但是在具体讲解 DLL 之前,通过一个静态链接库的例子可以快速地帮助 ...
- Shell中的变量替换
${str-newStr} 仅当str为null的时候替换 ${str:-newStr} 当str为null或者str为空字符串的时候替换 综上,-号,当str没有内容,则替换, :号只是增加了空字符 ...
- 关于final关键字
final修饰基本数据类型时 修饰的变量值不可变 final修饰引用数据类型时 修饰的变量地址不可变 值可变 final修饰一个类中的方法时 不可被子类重写 final修饰一个类时 不可被其他类继承 ...
- 手势仿QQ侧滑---秀清
// // SlideViewController.h // qqcehua // // Created by 张秀清 on 15/5/25. // Copyright (c) 2015年 张秀清. ...