iOS 개발일지
LiveActivity로 실시간 현황을 보여주자! (feat.DynamicIsland) 본문
LiveActivity란 앱 내의 정보를 잠금화면에서 보여줄 수 있는 기능입니다!
ActivityKit을 사용하며, iOS16.1 이상 버전에서 사용 가능합니다.
배달의 민족에서 많이 보셨을 것 같은데요~
뭔가 신기술같고... 멋있고... 이쁘지 않나요?
저는 개인적으로 좋아하던 기능이라, 직접 구현하려니 조금 신났네요ㅋㅋㅋㅋㅎ
아무튼, 단계별로 따라오면 전혀 어렵지 않습니다!
widget 자 우선 File -> New -> Target을 선택하고~
Widget을 검색해 추가해줍니다. Include Live Activity를 꼭 체크해주세요!
그리고 plist에 가서 Supports Live Activities를 추가하고, Value를 YES로 바꾸면 기본 셋팅은 끝!
각 파일 설명
AppIntent : 사용자가 위젯을 길게 눌러 [위젯 편집] 선택 시 보이는 정보를 구성하는 파일
Widget : Widget을 구성하는 파일
WidgetLiveActivity : LiveActivity와 DynamicIsland를 구성하는 파일
WidgetBundle : Widget 번들 파일
참고로, Widget Extension을 추가하면 Widget이 자동으로 추가됩니당
Live Activity만 구현하고 싶다면 WidgetBundle 파일에서 Widget을 주석 처리!
import WidgetKit
import SwiftUI
@main
struct TestWidgetBundle: WidgetBundle {
var body: some Widget {
// TestWidget()
TestWidgetLiveActivity()
}
}
import ActivityKit
import WidgetKit
import SwiftUI
struct TestWidgetAttributes: ActivityAttributes {
public struct ContentState: Codable, Hashable {
// 실시간 변동 값
var emoji: Int
}
// 고정 값
let name: String
}
@available(iOS 16.1, *)
struct TestWidgetLiveActivity: Widget {
var body: some WidgetConfiguration {
ActivityConfiguration(for: TestWidgetAttributes.self) { context in
// Lock screen/banner UI goes here
VStack {
Text("Hello \(context.attributes.name) \(context.state.emoji)")
}
.activityBackgroundTint(Color.cyan)
.activitySystemActionForegroundColor(Color.black)
} dynamicIsland: { context in
// 다이나믹 아일랜드
DynamicIsland {
// Expanded UI goes here. Compose the expanded UI through
// various regions, like leading/trailing/center/bottom
DynamicIslandExpandedRegion(.leading) {
Text("Leading")
}
DynamicIslandExpandedRegion(.trailing) {
Text("Trailing")
}
DynamicIslandExpandedRegion(.bottom) {
Text("Bottom \(context.state.emoji)")
// more content
}
} compactLeading: {
Text("L")
} compactTrailing: {
Text("T \(context.state.emoji)")
} minimal: {
Text(context.state.emoji)
}
.widgetURL(URL(string: "http://www.apple.com"))
.keylineTint(Color.red)
}
}
}
Attributes의 ContentState 내부에는 실시간으로 변동시킬 동적인 프로퍼티를 정의하고
그 아래에는 객체 생성 시 값을 한번만 초기화 할 정적인 프로퍼티를 정의하면 됩니다.
그리고 dynamicIsland를 볼 수 있는 기기라면, Live Activity가 활성화된 동안 dynamicIsland도 같이 보여주기 때문에 dynamicIsland도 구현해주어야 합니다.
이건 LiveActivity를 전부 끝낸 후 맨 아래에서 다시 보겠습니다.
방금 만든 이 LiveActivity 파일의 Target Membership에 Project를 추가해야 앱에서 접근이 가능하겠죠?
체크해줍시다.
Live Activity 시작하기
자 이제 실제로 Live Activity를 실행시켜보겠습니다.
저는 Live Activity를 시작할 ViewController가 여러개이기 때문에, 재사용이 가능하도록 Manager 파일을 따로 만들어줬어요.
import Foundation
import ActivityKit
@available(iOS 16.1, *)
@objc class TestLiveActivityManager: NSObject {
private var activity: Activity<TestWidgetAttributes>?
@objc static let shared = TestLiveActivityManager()
private init(activity: Activity<TestWidgetAttributes>? = nil) {
self.activity = activity
}
@objc func onLiveActivity() {
guard ActivityAuthorizationInfo().areActivitiesEnabled else { return }
// 정적 프로퍼티 초기화
let attribute = TestWidgetAttributes(name: "name")
// 동적 프로퍼티 기본값 초기화
let state = TestWidgetAttributes.ContentState(emoji: "emoji")
do {
// live activity 시작
self.activity = try Activity.request(attributes: attribute, contentState: state)
} catch {
print(error)
}
}
@objc func offLiveActivity() {
Task {
await activity?.end(using: nil, dismissalPolicy: .immediate)
}
}
@objc func updateLiveActivity(emoji: String) {
Task {
let newState = TestWidgetAttributes.ContentState(emoji: emoji)
await self.activity?.update(using: newState)
}
}
특정 시점에 LiveActivity가 바로 닫히길 원한다면, end에서 dismissalPolicy를 .immediate로 설정해주세요!
여기까지 왔다면 이제 사용하고자 하는 시점에서 이렇게 호출해주기만 하면 됩니다. 너무 간단하죠?
TestLiveActivityManager.shared.onLiveActivity()
TestLiveActivityManager.shared.offLiveActivity()
TestLiveActivityManager.shared.updateLiveActivity(emoji: "smail")
Dynamic Island 구성하기
다이나믹 아일랜드는 크게 expanded, compact, minimal 세 가지 형태가 있습니다.
expanded 상태에서는 leading, trailing, center, bottom 으로 영역이 나뉘어져 있고
compact 상태에서는 사이드에 각각 leading side, trailing side가 있습니다.
마지막으로 다이나믹 아일랜드를 사용하는 또 다른 앱이 있을 경우, 한쪽 사이드를 내어주어야 하기 때문에 한 공간만 차지할 수 있습니다.
} dynamicIsland: { context in
DynamicIsland {
// Expanded UI goes here. Compose the expanded UI through
// various regions, like leading/trailing/center/bottom
DynamicIslandExpandedRegion(.leading) {
Text("Leading")
}
DynamicIslandExpandedRegion(.trailing) {
Text("Trailing")
}
DynamicIslandExpandedRegion(.bottom) {
Text("Bottom \(context.state.emoji)")
// more content
}
} compactLeading: {
Text("L")
} compactTrailing: {
Text("T \(context.state.emoji)")
} minimal: {
Text(context.state.emoji)
}
.widgetURL(URL(string: "http://www.apple.com"))
.keylineTint(Color.red)
}
처음 Live Activity 파일이 생성되면 기본적으로 작성되어있는 코드입니다.
깔끔하게 정리되어 있어서 그림과 함께 보면 한눈에 봐도 알기 쉽죠?
틀린 부분이나 궁금하신 점 있으면 댓글로 알려주세요 :)
'iOS' 카테고리의 다른 글
Cell이 선택되었을 때 UI 변경, didSeletAt 말고 딴거 써보자! (+Cell의 zPosition 변경) (0) | 2024.02.02 |
---|---|
git push가 안될 때!! hint: Updates were rejected because the tag already exists in the remote. (0) | 2023.12.15 |
[iOS] WKWebView의 현재 URL을 감지하고, 이벤트를 발생시키자! (0) | 2023.11.27 |
(비공개) (0) | 2023.06.15 |
Objective-C에 대해 아주아주 간단히 알아보자 (0) | 2023.03.08 |