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) -> ()) {}
}
//模型
//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?, 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.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

你可能感兴趣的:(WidgetKit&ActivityKit)