背景
这篇文章主要做一个引导,以及开发过程中遇到的疑难点进行讲述。具体的实操部分网上已经很多文章,可以去找找。
开发灵动岛必看官方文档:
- 灵动岛设计开发规范 ,这篇文档你们UI和产品会需要,你也需要看一下,讲述了灵动岛的尺寸、样式等。《Live Activities》
- 灵动岛开发指导 ,这篇文章主要讲解灵动岛的相关接口使用,开发必看。《Displaying live data with Live Activities》
问题点
1、灵动岛和实时活动如何实现动画?
目前灵动岛和实时活动还不支持自定义动画,如果自己写了动画,会被系统强制屏蔽。但iOS17开始,可以使用时序曲线实现一些线性的动画效果,具体可以去了解一下。
2、如何屏蔽或者修改系统自带的模糊淡入和淡出的动画效果?
可以使用控件的转场动画.contentTransition()接口,对内容动画进行调整。注意.transition()和.contentTransition()方法的区别,.transition()方法是针对整个控件的转场动画,如果只是控件内容发生改变,不会有什么效果的;.contentTransition()是对内容的转场动画,比如更新Text的内容,就用到它。.identity屏蔽系统动画,还有其他枚举,可以调整动画显示的类型。
scss
//屏蔽系统自带的动画淡入淡出效果
Text(context.state.time)
.multilineTextAlignment(.center)
.foregroundColor(context.state.color)
.font(.system(size: 14).weight(.semibold))
.padding(.trailing, 7)
.contentTransition(.identity)
3、如何在灵动岛或者实时活动添加按钮?
iOS17及以上才支持添加按钮点击事件,在此之前添加按钮到灵动岛或者实时活动页上,点击按钮时,点击事件是不生效的,会直接打开App。
4、灵动岛/实时活动如何和主App进行通讯,传递事件或者数据?
灵动岛/实时活动和主App进行通讯,分为两个方向的数据传递:主App-->灵动岛 ,灵动岛-->主App 。 讲这个前,首先我们要清楚一个前提:当主App被杀掉时(没运行的时候),灵动岛和实时活动仍然是可以展示和更新的,而灵动岛和实时活动作为一个小组件,并不知道主App的生命周期。因此灵动岛-->主App同步发送数据或事件这条链路是行不通的。 那问题来了,我们要怎么实现数据传递呢?无法同步实现,我们可以异步实现是不?
- 主App-->灵动岛
这个链路就比较容易实现,苹果提供了相关的接口:
swift
/// 显示灵动岛和实时活动
@objc public func show(title:String, time:String, islandIcon:String, islandColor:String, content:String, isRecording: Bool) {
let attributes = CCityWidgetAttributes(title: title)
let contentState = CCityWidgetAttributes.ContentState(time: time, islandIcon: islandIcon, islandColor: islandColor, content: content, isRecording: isRecording)
do {
activity = try Activity<CCityWidgetAttributes>.request(attributes: attributes, contentState: contentState, pushType: nil)
} catch {
print(error.localizedDescription)
}
}
/// 更新灵动岛内容
@objc public func update(time:String, islandIcon:String, islandColor:String, content:String, isRecording: Bool) {
let contentState = CCityWidgetAttributes.ContentState(time: time, islandIcon: islandIcon, islandColor:islandColor, content: content, isRecording: isRecording)
Task {
await self.activity?.update(using: contentState)
}
}
/// 结束灵动岛显示
@objc public func end() {
Task {
for activity in Activity<CCityWidgetAttributes>.activities{
await activity.end(dismissalPolicy: .immediate)
}
}
}
swift
@available(iOS 16.1, *)
struct CCityWidgetAttributes: ActivityAttributes {
public struct ContentState: Codable, Hashable {
// 动态属性,可以在update方法中更新的属性,需要一直更新的内容,可以放到ContentState中。
var time: String
var islandIcon: String
var islandColor: String
var content: String
var isRecording: Bool
var color: Color {
return Color(hexString: islandColor)
}
}
// 静态属性,只有在show的时候才会更新的属性,不变的属性放在这里
var title: String
}
- 灵动岛-->主App
这个链路目前苹果并没有提供直接同步传递数据的方式,因此是不支持这个链路的数据通讯的。但是我们可以使用共享沙盒目录进行数据传递,即当用户点击灵动岛或者实时活动中的按钮时,将状态或者数据保存到共享沙盒中,然后当唤起主App时,主App读取沙盒中的数据,实现数据传递。相关逻辑可以参考《APP Extension 与 APP之间的数据共享》。
当然网上还有其他方案,比如通过本地socket进行数据发送来实现通讯,大致思路就是小组件和主App,作为socket的两端,相互发送数据来实现通讯,但是那样太重,没这必要。至于其他方式基本都被苹果禁用掉了,苹果也拒绝使用其他非常规手段,建议不用徒劳了。
4. 其他一些开发过程中遇到的问题。
- 连续同步update灵动岛,数据只会更新一次,苹果内部接口应该对灵动岛的更新有一定的时间限制,因此建议将数据缓存一次性进行更新。
- 当锁屏时(真正的锁屏,即没有解锁脸部识别),实时活动的更新频率会降低,比如几秒钟才会更新一次(哪怕你代码是每秒钟都调update接口),且更新动画也会被屏蔽,苹果应该是为了省电吧。