iOS16.1系統(tǒng)開始正式支持實(shí)時(shí)活動(dòng),更新Xcode為最新的14.1版本,(跟著官方文檔步驟)開始著手開發(fā)
官方文檔https://developer.apple.com/documentation/activitykit/displaying-live-data-with-live-activities
前情提要:項(xiàng)目是OC項(xiàng)目,且這次實(shí)時(shí)活動(dòng)沒有用到遠(yuǎn)程推送
1.新建OC項(xiàng)目,Info-Plist文件加入Bool類型的權(quán)限key值為:Supports Live Activities value為:YES
2.如果項(xiàng)目之前有過小組件,則不需要再創(chuàng)建新的Widget-Extension,直接在@main WidgetBundle里面添加要?jiǎng)?chuàng)建的Live-Activity組件即可(我們?cè)诤竺嫣砑樱?/h5>
3.(我是OC項(xiàng)目)在主項(xiàng)目里面新建一個(gè)Attributes.swift項(xiàng)目
Attributes
Attributes官方文檔有詳細(xì)講解,以我的為例
import Foundation
import ActivityKit
//我自己的Widget類
struct ZKWidgetAttributes: ActivityAttributes {
public typealias ZKWidgetState = ContentState
//可變參數(shù)(動(dòng)態(tài)參數(shù))
public struct ContentState: Codable, Hashable {
//參數(shù),當(dāng)前的監(jiān)測(cè)時(shí)間
var monitorTime: ClosedRange<Date>
var sleepProgress :Float
}
var sleepGoal: Double
}
//定義一個(gè)Manager類(單例)
@available(iOS 16.1, *)
public class ZkActivityManager : NSObject{
var timer: Timer?
var sleepTime: Int = 0
var progress: Float = 0.0
static let defaultManager: ZkActivityManager = ZkActivityManager()
@objc public class func defaultManage() -> ZkActivityManager
{
return ZkActivityManager.defaultManager
}
//開啟一個(gè)實(shí)時(shí)活動(dòng)
@objc public func startActivity(sleepGoal:Int){
self.startTimer()
let attributes = ZKWidgetAttributes(sleepGoal: Double(sleepGoal))
let initialConetntState = ZKWidgetAttributes.ZKWidgetState(monitorTime: Date()...Date().addingTimeInterval(12 * 60 * 60 ),sleepProgress: Float(self.progress))
do {
let activity = try Activity<ZKWidgetAttributes>.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)")
}
}
//更新一個(gè)實(shí)時(shí)活動(dòng)
@objc public func updateActivity (){
Task {
let updateStatus = ZKWidgetAttributes.ZKWidgetState(monitorTime: Date()...Date().addingTimeInterval(0.2 * 60),sleepProgress:Float(self.progress))
for activity in Activity<ZKWidgetAttributes>.activities{
await activity.update(using: updateStatus)
}
}
}
//結(jié)束一個(gè)實(shí)時(shí)活動(dòng)
@objc public func stopActivity (){
Task {
for activity in Activity<ZKWidgetAttributes>.activities{
await activity.end(dismissalPolicy: .immediate)
}
}
self.cancelTimer()
}
//是否支持實(shí)施活動(dòng)(開關(guān)是否打開)
@objc public func isOpenLiveActivity ()-> Bool {
if !ActivityAuthorizationInfo().areActivitiesEnabled {
return false
}
return true
}
//獲取實(shí)施活動(dòng)列表
@objc public func getLiveActivityList () async{
for await activity in Activity<ZKWidgetAttributes>.activityUpdates {
print("Pizza delivery details: \(activity.attributes)")
}
}
//開啟定時(shí)器
@objc public func startTimer() {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(recordTime), userInfo: nil, repeats: true)
if let curTimer : Timer = timer {
RunLoop.main.add(curTimer, forMode: .common)
}
}
@objc func recordTime(){
sleepTime += 1
self.progress = Float(sleepTime) / 480.0 / 60
print("開始計(jì)時(shí):\(self.sleepTime)")
print("目前進(jìn)度:\(self.progress)")
}
deinit {
cancelTimer()
}
func cancelTimer() {
timer?.invalidate()
timer = nil
}
}
tips 需要在Widget里用到我們?cè)谥鞴こ痰腁ttributes,需要在Attributes.swift文件右邊的Target Membership里面,勾上WidgetExtension,以確保組件也可以用到該文件
4.創(chuàng)建Widget
里面包括鎖屏界面的Widget以及靈動(dòng)島的Widget,這塊就自己用SWiftUI進(jìn)行布局即可
//當(dāng)然我們也可以通過不同的Attributes開啟不同的實(shí)時(shí)活動(dòng),傳入不同參數(shù),刻畫不同的Widget樣式
5.上截圖


放到最后
1.自己心中有個(gè)疑慮,如果實(shí)時(shí)活動(dòng)鎖屏組件上有個(gè)類似于圓環(huán)進(jìn)度條,如何做到實(shí)時(shí)刷新(沒有remote-push)),本人想法是,主項(xiàng)目有個(gè)計(jì)時(shí)器,然后調(diào)用updateActivity方法,但是可能會(huì)導(dǎo)致大量耗電,不是一個(gè)好的方法。
Text有一個(gè)(timerInterval: ClosedRange<Date>, pauseTime: Date? = nil, countsDown: Bool = true, showsHours: Bool = true)方法,可以動(dòng)畫的進(jìn)行倒計(jì)時(shí),也就是Apple官方給的披薩外賣demo。
就先到這,后期有啥再更新!
Github地址https://github.com/Superman-Frank/liveActivity