LiveActivity使用和介绍

1.简单介绍

    ActivityKit是2022年新发布的,在开始iOS16上提供的新功能:提供实时活动的预览能力,在灵动岛和锁定屏幕、消息中心上显示应用程序的最新数据,能够大大提升用户获取相关实时信息的能力。其中也存在以下的限制和不足之处:

    • 整个灵动岛活动最多只能维持八个小时的时间

    • 可以通过 ActivityKit 或者 ActivityKit push notifications 来配置、启动、更新与终止 Live Activity但二者在更新时的动态数据大小均不能超过 4 KB

    • 每个 Activity 对应一个推送token,Activity 中止后,令牌失效

    • 可以通过Link实现点击不同区域,跳转到不同页面。如果只使用widgetURL的方式跳转,那么仅可以跳转到一个scheme页面。

2.应用场景

应用场景主要是需要给用户一个入口快速查看活动的进度,使用户不用点进app就能看到这个活动的进度,官方文档是采用一个送披萨的例子,用户外卖点了披萨配送,这时候app可以申请一个 LiveActivity ,通过常驻的消息中心和灵动岛来显示实时进度,并且在披萨到达以后可以推送通知。目前主要在体育赛事记分展示上、计时应用、当前播放的音乐内容、显示相关导航信息、上传/下载速度和进度等这些应用中可以相关功能。

3.展示类型和界面区域划分

  • 1.紧凑型

在灵动岛中,当只有一个当前处于活动状态的实时活动时,系统使用紧凑的展示。紧凑的展示由 2 个单独的展示组成:其中 1 个显示在原深感摄像头前侧,另 1 个显示在后侧。虽然前侧展示和后侧展示是单独的视图,但它们在灵动岛中形成了一个结合的视图,代表了 app 中的一条单独的信息。用户可通过轻按紧凑的实时活动来打开 app,并获取关于事件或任务的更多详细信息。


紧凑型内容区域

对应的代码参数界面


//左侧Lead样式
compactLeading: {
  // 紧凑型样式左边 UI
  Text("灵动岛未展开的左边")
} compactTrailing: {//右侧Trail样式
  // 紧凑型样式右边 UI
  Text("灵动岛未展开的右边 \(context.state.emoji)")
}
  • 2.最小型

当多个实时活动处于活动状态时,系统通过使用最小的展示来在灵动岛中显示其中的 2 个活动。其中 1 个实时活动显示为附属到动态岛,另 1 个显示为与灵动岛分离。根据其陆内容大小,分离状态的最小实时活动显示为圆形或椭圆形。与紧凑的灵动岛一样,用户通过轻按最小的实时活动来打开 app,用户长按也是可以讲其由最小型切换到扩展型 ,并获取关于事件或任务的更多详细信息。最多支持两个灵动岛同时进行展示,其中采取先来后到的原则,最后开始的灵动岛是attached的状态,之前开始的就是detached的状态,点击长按的时候展示扩展型

最小型内容区域.png

对应的代码参数界面

minimal: {
    // 最小型样式 UI
    Text("灵动岛Mini \(context.state.emoji)")
}
  • 3.扩展型

当用户在紧凑或最小的展示中长按 1 个实时活动时,系统会以扩展展示的方式显示内容。点击紧凑型之后就会显示扩展型的,扩展型的每个区域的如下,

扩展型区域划分.png

1.center将内容放置在TrueDepth相机下方。
2.leading将内容放置在TrueDepth相机旁边的扩展Live Activity的前缘,并在其下方包装其他内容。
3.Trailing将内容放置在TrueDepth相机旁边的扩展Live Activity的后缘,并在其下方包装额外的内容。
4.Bottom将内容置于前导、尾随和居中内容之下。

效果展示.png

对应的代码参数界面

            DynamicIsland {
                // 展开样式的 UI界面
                DynamicIslandExpandedRegion(.leading) {
                    Text("灵动岛展开的左边")
                }
                DynamicIslandExpandedRegion(.trailing) {
                    Text("灵动岛展开的右边")
                }
                DynamicIslandExpandedRegion(.bottom) {
                    Text("灵动岛展开的地步 \(context.state.emoji)")
                    // more content
                }
            }
  • 4.锁屏(Lock Screen)

在锁屏和不支持灵动岛的设备上,系统通过使用锁屏展示来在屏幕顶部显示横幅。锁屏展示会在用户查看主屏幕或使用另 1 个 app 时短暂出现,但仅限于 app 确定这个更新足够重要到可以打断用户。锁屏展示界面的用户,需要在第一次使用的时候给予始终允许的权限,否则会导致锁屏界面刷新不及时,出现不同步的问题


锁屏界面.png

对应的代码参数界面

        ActivityConfiguration(for: KPWidgetAttributes.self) { context in
            // Lock screen/banner UI goes here            HStack {
                VStack (alignment: .leading){
                    HStack {
                        Image("ic_get_up")
                        Text("Time to get up now")
                            .fontWeight(.medium)
                            .font(.system(size: 16))
                            .foregroundColor(Color(hex: "666666"))
                            .padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
                    }
                    Text(timerInterval: context.state.monitorTime,countsDown: false)
                        .fontWeight(.semibold)
                        .font(.system(size: 36))
                        .padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
                    }
                Spacer()
                    
                ZStack (alignment: .center){
                    Text("sdsadsadsasa2")
                    .padding(EdgeInsets(top: 10, leading: 20, bottom: 10, trailing: 20))
                    .background (Color(hex: "333333",alpha: 0.16))
                    .cornerRadius(22)
                    .border(Color(hex: "333333",alpha: 0.16),width: 0)
                }.padding(EdgeInsets(top: 10, leading: 0, bottom: -30, trailing: 0))
            }.padding(EdgeInsets(top: 30, leading: 20, bottom: 30, trailing: 20))
            .activityBackgroundTint(Color(hex: "F7DFC6"))
            .activitySystemActionForegroundColor(nil)
            
        }

4.展示界面的参数规格

下面是一个设备在使用liveActivity的情况下,相关状态下的页面大小的参照参数


图片.png

规则参数大小.png

5.使用介绍

  • 配置权限

1.在主工程项目中,Info-Plist文件加入Bool类型的权限key值为:Supports Live Activities value 为:YES


图片.png

2.在手机系统通用设置界面中->找到该App->打开支持实时活动开关


图片.png
  • 创建

由于ActivityKit是基于widgetKit的,所以接下来是给我们的项目创建一个小组件extension,选择创建文件的路径File->Target->WidgetExtension,在创建Extension的时候,在


创建流程.png

主入口


@mainstruct LiveActTestBundle: WidgetBundle {
    var body: some Widget {
        LiveActTest()
        LiveActTestLiveActivity()
    }}

搭建展示界面视图


struct LiveActTestLiveActivity: Widget {
    var body: some WidgetConfiguration {
        ActivityConfiguration(for: KPWidgetAttributes.self) { context in
            // 锁屏状态下的界面展示信息
            HStack {
                VStack (alignment: .leading){
                    HStack {
                        Image("ic_get_up")
                        Text("Time to get up now")
                            .fontWeight(.medium)
                            .font(.system(size: 16))
                            .foregroundColor(Color(hex: "666666"))
                            .padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
                    }
                    Text(timerInterval: context.state.monitorTime,countsDown: false)
                        .fontWeight(.semibold)
                        .font(.system(size: 36))
                        .padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
                    }
                Spacer()
                    
                ZStack (alignment: .center){
                    Text("sdsadsadsasa2")
                    .padding(EdgeInsets(top: 10, leading: 20, bottom: 10, trailing: 20))
                    .background (Color(hex: "333333",alpha: 0.16))
                    .cornerRadius(22)
                    .border(Color(hex: "333333",alpha: 0.16),width: 0)
                }.padding(EdgeInsets(top: 10, leading: 0, bottom: -30, trailing: 0))
            }.padding(EdgeInsets(top: 30, leading: 20, bottom: 30, trailing: 20))
            .activityBackgroundTint(Color(hex: "F7DFC6"))
            .activitySystemActionForegroundColor(nil)
            
        } dynamicIsland: { context in
            DynamicIsland {
                ///点击之后的展开状态----展开之后分为不同的区域,大概总共三个区域
                // Expanded UI goes here.  Compose the expanded UI through
                // various regions, like leading/trailing/center/bottom
                DynamicIslandExpandedRegion(.leading) {
                    VStack (alignment: .leading){
                        HStack {
                            Image("ic_in_bed")
                            Text("DynamicIsland1")
                                .fontWeight(.medium)
                                .font(.system(size: 16))
                                .foregroundColor(Color(hex: "FFFFFF",alpha:0.5))
                        }
                        Text(timerInterval: context.state.monitorTime,countsDown: false)
                            .fontWeight(.semibold)
                            .font(.system(size: 18))
                            .foregroundColor(Color(hex: "FFFFFF"))
                    
                    }.padding(EdgeInsets(top: 0, leading: 5, bottom: 0, trailing: 0))
                }
                DynamicIslandExpandedRegion(.trailing) {
                    ZStack (alignment: .center){
                        Text(NSLocalizedString("Stop", comment: ""))
                        .fontWeight(.bold)
                        .font(.system(size: 14))

                    }.padding(EdgeInsets(top: 30, leading: 0, bottom: 0, trailing:5))
                    
                }
                DynamicIslandExpandedRegion(.bottom) {
                    // more content
                }
            } compactLeading: {
                ///单个收起状态左侧
                Image("ic_in_bed")
                    .padding(EdgeInsets(top: 0, leading: 10, bottom: 0, trailing: 0))
            } compactTrailing: {
                ///单个收起状态右侧
                Text(timerInterval: context.state.monitorTime,countsDown: false)
                    .fontWeight(.semibold)
                    .font(.system(size: 18))
                    .padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 10))
            } minimal: {
                //最小话状态
                Image("ic_in_bed")
            }
            .widgetURL(URL(string: "http://www.apple.com"))
            .keylineTint(Color.gray)
        }
    }}

如果项目本身是swift项目,那么直接在主工程中直接去调用对应的类即可,但是如果主工程是oc项目,那么就需要oc调用swift中的类

  • oc调用swift

找到工程文件下的TARGETS -->Build settings中搜索 Product Module Name设置为工程名,这时工程会自动创建一个"项目名"-Swift.h的文件,最后在OC文件需要使用的地方里面#import "项目名称-Swift.h", 注意这个文件没有提示,要手动打出来,然后就能在OC类里面使用swift的类了,创建ActivityAttributes类和ActivityManager管理类,将其中的管理方法和展示方法放在主工程中去,在需要调用的地方引用"项目名"-Swift.h的文件,实现oc对swift中管理类的调用


struct KPWidgetAttributes:ActivityAttributes{//当前需要用到的参数
    
    public typealias KPWidgetState = ContentState
    
    public struct ContentState: Codable, Hashable {
        //参数,当前的监测时间
        var monitorTime: ClosedRange<Date>
        var sleepProgress: Double
        var activityType: Bool
    }
    
    var sleepGoal: Double}

    ///启动活动
    @objc public func startActivity(sleepGoal:Int,currentDate:Date,type:Bool){
        self.startTimer()
        
        let attributes = KPWidgetAttributes(sleepGoal: Double(sleepGoal))
        
        let initialConetntState = KPWidgetAttributes.KPWidgetState(monitorTime: StartDate...Date().addingTimeInterval(12 * 60 * 60 ),sleepProgress: kpActivityManager.progress, activityType: type)
        
        do {
            let activity = try Activity<KPWidgetAttributes>.request(attributes: attributes, contentState: initialConetntState, pushType: nil)
            print("Requested a pizza delivery Live Activity \(activity.id)")
        } catch (let error) {
            print("Error requesting pizza delivery Live Activity \(error.localizedDescription)")
        }
        
    }

    ///停止活动
    @objc public func stopActivity (){
        
        Task {
            for activity in Activity<KPWidgetAttributes>.activities{
                await activity.end(dismissalPolicy: .immediate)
            }
        }
        self.cancelTimer()
    }

6.最终效果展示预览

效果预览.gif

参考文章

1.官方文档
2.iOS16 基于ObjectiveC的实时活动以及灵动岛
3.iOS 小组件开发第八篇:灵动岛开发
4.SwiftUI 官方教程

iOS16.1 实时活动 (Live Activity)&灵动岛适配-CSDN博客

Powered By Z-BlogPHP 1.7.3

Copyright 粤ICP备2024347557号 Rights Reserved.