WidgetKit&ActivityKit

1、概念

1.1 在主屏幕、通知中心、锁屏界面(iOS16)显示的小组件
1.2 小组件的样式大小是固定的,iOS下目前有6中样式,可读取环境变量获取widgetFamily

image.png

image.png

image.png

1.3小组件支持点击跳转、支持基础控件,不支持滑动和交互
小组件的点击默认是打开app
可以通过widgetURL或者Link(支持medium、large)去区分点击事件

.onOpenURL { url in
   print("\(url)")
}

1.4 iOS锁屏界面小组件的渲染模式是vibrant,去饱和度显示,可读取环境变量获取widgetRenderingMode

image.png

1.5 小组件只支持SwiftUI部分控件,不支持桥接的UIKit控件,OC应用也可以添加小组件
1.6 用户必须在安装应用程序后至少启动一次才可以使用小组件

2、使用

2.1 创建Widget Extension

image.png
image.png

复选框表示是否需要自定义小组件,如果是使用静态小组件配置,请不要勾选这个选项。

2.2 点击finish生成小组件模版

image.png
//1、kind:小组件的名称,用于区分小组件,app刷新小组件
//2、intent:可编辑小组件的配置,编译后编译器生成的
//3、provider:timelineProvider,提供占位,预览,显示数据
//4、content:显示数据的试图
//5、displayName:小组件的展示名称,在预览页会显示
//6、description:小组件的描述,在预览页会显示
@main
struct IntentWidget: Widget {
    var body: some WidgetConfiguration {
        IntentConfiguration(kind:  "IntentWidget", intent: ConfigurationIntent.self, provider: Provider()) { entry in
            IntentWidgetEntryView(entry: entry)
        }
        .configurationDisplayName("My Widget")
        .description("This is an example widget.")
    }
}
struct Provider: IntentTimelineProvider {
//占位数据,添加小组件之后的占位数据,首次才会显示
//占位数据显示的时机:
//1、timeline提供的数据有延时时
//2、默认是显示骨架图的,需要调用控件的`.unredacted()`
    func placeholder(in context: Context) -> SimpleEntry {}
//预览图,提供准备添加小组件时显示的数据
    func getSnapshot(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (SimpleEntry) -> ()) {}
//提供数据,以及刷新策略`TimelineReloadPolicy`
//1.atEnd:timeline entries 全部显示后重新加载timeline
//2.never:不需要重新加载timeline
//3.after(_ date: Date):在未来的日期后重新加载timeline
    func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {}
}
//模型
//date:在这个时间使用这个模型
struct SimpleEntry: TimelineEntry {
    let date: Date
    let configuration: ConfigurationIntent
    var relevance: TimelineEntryRelevance?//控制在堆栈小组件中将当前小组件滚动到顶端
}
//视图
//通过环境变量获取当前显示的小组件的类型
struct IntentWidgetEntryView : View {
    var entry: Provider.Entry
    @Environment(\.widgetFamily) var family
    var body: some View {
        Text(entry.date, style: .time)
    }
}
image.png

2.4 配置intent

image.png
image.png
image.png
image.png
class IntentHandler: INExtension, ConfigurationIntentHandling {
    func provideNameOptionsCollection(for intent: ConfigurationIntent, with completion: @escaping (INObjectCollection<NSString>?, Error?) -> Void) {
        completion(INObjectCollection(items:["aaa","bbb","ccc"]), nil)
    }
    
    override func handler(for intent: INIntent) -> Any {
        return self
    }
}

注意事项:IntentHandler需要实现ConfigurationIntent协议,会出现找不到协议的error,原因是ConfigurationIntent编译后的类没有加入到IntentHandler的target中

image.png

3 其他

3.1 小组件刷新

1、widgetkit刷新频次最小间隔5分钟
2、为可预测的事件生成时间线getTimeline
3、应用内刷新小组件

WidgetCenter.shared.reloadTimelines(ofKind: "com.mygame.character-detail")
WidgetCenter.shared.reloadAllTimelines()

3.2 WidgetBundle

//最多五个,可以嵌套使用
@main
struct GameWidgets: WidgetBundle {
    @WidgetBundleBuilder
    var body: some Widget {
        GameStatusWidget()
        CharacterDetailWidget()
   }
}

3.3 Live Activities

image.png

image.png

1.只支持iPhone
2.使用WidgetKit & SwiftUI
3.更新的动态数据最大4KB
4.最大高度160pt
5.内容更新时自动显示动画
6.必须在应用内开启Live Activity
7.结束或者更新Live Activity可以在应用内或者pushNotification
8.最大活跃时间是8个小时,结束之后最大可以展示4个小时
9.info.plist 配置NSSupportsLiveActivities 为true

1、创建widget

import ActivityKit
import SwiftUI
import WidgetKit

struct PizzaDeliveryAttributes: ActivityAttributes {
    public typealias PizzaDeliveryStatus = ContentState

    public struct ContentState: Codable, Hashable {
        var driverName: String
        var estimatedDeliveryTime: Date
    }

    var numberOfPizzas: Int
    var totalAmount: String
}

struct PizzaDeliveryActivityWidget: Widget {
    var body: some WidgetConfiguration {
        ActivityConfiguration(attributesType: PizzaDeliveryAttributes.self) { context in
            // Create the Lock Screen user interface for your Live Activity.
        VStack {
                Text("\(context.attributes.numberOfPizzas) ordered for \(context.attributes.totalAmount).")
                HStack {
                    Text("\(context.state.driverName) is on their way with your pizza!")
                    Text(context.state.estimatedDeliveryTime, style: .timer)
                }
            }.activityBackgroundTint(Color.cyan)
        }
    }
}

2、开启LiveActivity

let pizzaDeliveryAttributes = PizzaDeliveryAttributes(numberOfPizzas: 42, totalAmount:"$420,-")

// Estimated delivery time is one hour from now.
let initialContentState = PizzaDeliveryAttributes.PizzaDeliveryStatus(driverName: "Bill James", estimatedDeliveryTime: Date().addingTimeInterval(60 * 60))
                                          
do {
    let deliveryActivity = try Activity<PizzaDeliveryAttributes>.request(
        attributes: pizzaDeliveryAttributes,
        contentState: initialContentState,
        pushType: nil)
    print("Requested a pizza delivery Live Activity \(deliveryActivity.id)")
} catch (let error) {
    print("Error requesting pizza delivery Live Activity \(error.localizedDescription)")
}

3、更新LiveActivity

let updatedDeliveryStatus = PizzaDeliveryStatus(driverName: "Anne Johnson", estimatedDeliveryTime: Date().addingTimeInterval(60 * 60))

do {
    try await deliveryActivity.update(using: updatedDeliveryStatus)
} catch(let error) {
    print("Error updating activity \(error.localizedDescription)")
}

4、结束LiveActivity

let updatedDeliveryStatus = PizzaDeliveryStatus(driverName: "Anne Johnson", estimatedDeliveryTime: Date())

do {
    try await deliveryActivity.end(using: updatedDeliveryStatus, dismissalPolicy: .default)
} catch(let error) {
    print("Error ending activity \(error.localizedDescription)")
}

ActivityUIDismissalPolicy
static let default: ActivityUIDismissalPolicy
static let immediate: ActivityUIDismissalPolicy
static func after(Date) -> ActivityUIDismissalPolicy

4、远程推送更新&结束LiveActivity

{
    "aps": {
        "timestamp": 1650998941,
        "event": "update",
        "content-state": {
            "driverName": "Anne Johnson",
            "estimatedDeliveryTime": 1659416400
        }
    }
}

4、参考链接

https://developer.apple.com/documentation/widgetkit
https://developer.apple.com/videos/all-videos/?q=WidgetKit/
https://medium.com/kinandcartacreated/widgetkit-advanced-development-part-1-dbb0e49e849c
https://medium.com/kinandcartacreated/widgetkit-advanced-development-part-2-a675a617fdc9
https://developer.apple.com/documentation/activitykit/displaying-live-data-on-the-lock-screen-with-live-activities

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,108评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,699评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,812评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,236评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,583评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,739评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,957评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,704评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,447评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,643评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,133评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,486评论 3 256
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,151评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,889评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,782评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,681评论 2 272

推荐阅读更多精彩内容