ข่าว

How to make Live Activities and Dynamic Island iOS 16.1

ผู้เขียน : Surachet Y.08 ธ.ค. 65อ่าน : 0MediumMedium
Surachet Y.
08 ธ.ค. 65อ่าน : 0Medium
How to make Live Activities and Dynamic Island iOS 16.1

ในงาน WWDC 2022 ได้มีการเปิดตัวฟีเจอร์ใหม่ขึ้นมาที่ชื่อว่า Live activity ซึ่ง Live activity เป็นเหมือน Notification รูปแบบหนึ่งที่อยู่บนหน้าจอล็อกสกรีน ข้อดีคือสามารถอัปเดตข้อมูลได้แบบเรียลไทม์ ยกตัวอย่างเช่น สั่งอาหารจากร้านค้าเราสามารถตรวจสอบได้ว่ามีสถานะเป็นอย่างไรบ้างแล้ว อีกนานเท่าไหร่ถึงจะได้รับอาหาร หรือตัวอย่างหนึ่งก็คือ เราต้องการที่จะดูผลกีฬาที่กำลังแข่งขันอยู่เราก็สามารถทราบผลของกีฬาที่กำลังแข่งขันอยู่ได้แบบเรียลไทม์

ในวันที่มีการเปิดตัว iPhone 14 ได้มีนวัตกรรมใหม่จากทาง Apple ถูกปล่อยมาใหม่นั่นคือ Dynamic Island ซึ่งจะอยู่ใน iPhone 14 Pro และ iPhone 14 Pro Max เท่านั้น โดย Dynamic Island มีด้วยกัน 3 รูปแบบ

Expanded เป็นรูปแบบที่แสดงข้อมูลได้มากที่สุด และเป็นเพียงรูปแบบเดียวที่สามารถเพิ่มปุ่มเพื่อเพิ่มการทำงานอะไรบางอย่างให้ได้

Compact เป็นรูปแบบมาตรฐานของ dynamic island เมื่อมีการเรียกการใช้งาน เมื่อทำการกดที่ dynamic island จะทำการเข้าไปยังแอพที่ทำการเปิด dynamic island

minimal โหมดนี้จะทำงานก็ต่อเมื่อมีการทำงานของ live activity 2 แอพโดย 2 ส่วน

ซ้าย เป็นส่วนที่ live activity ถูกทำงานล่าสุด

ขวา เป็นส่วนที่ live activity ทำงานอยู่ก่อนแล้ว และมีการทำงานที่แอพอื่นเข้ามา

Let’s Start

Step 1

เพิ่ม Widget Extension ไปยังโปรเจ็กต์ของคุณ (File -> New -> Target -> Widget Extension)

💡 อย่าลืมเช็ค Include Live Activity ถูกเช็คไว้ไหม

Step 2

เพิ่ม NSSupportsLiveActivities ที่มีไทป์เป็นBoolean และต้องมีค่าเป็นYES ใน Info.plist

Step 3

หลังจากสร้าง Widget Extension แล้วเราจะได้ Files ให้เราสร้าง Attributes สำหรับใช้ใน Live Activity

import ActivityKit

public struct FootballMatch: ActivityAttributes {
// ContentState เป็น model ในส่วนที่มีการเปลี่ยนแปลงได้ตลอดเวลา
public typealias ContentState = Score

public struct Score: Codable, Hashable {
let score1: Int
let score2: Int
let information: String
}
// นอกจากที่อยู่ใน ContentState จะเป็นค่าที่ไม่ต้องการให้เปลี่ยนแปลง
let fullNameFirstTeam: String
let shortNameFirstTeam: String
let firstTeamLogo: String
let fullNameSecondTeam: String
let shortNameSecondTeam: String
let secondTeamLogo: String
}

Step 4

สร้างหน้าตาให้กับ Live Activity โดยจะแบ่งเป็นสองส่วนด้วยกัน คือ ส่วนของ Live Activity และ ส่วนของ Dynamic Island


struct LiveScoreWidgetLiveActivity: Widget {
var body: some WidgetConfiguration {
ActivityConfiguration(for: FootballMatch.self) { context in
// Lock screen/banner UI goes here
LockScreenLiveActivityView(context: context)
} dynamicIsland: { context in
DynamicIsland {
// Expanded UI goes here. Compose the expanded UI through
// various regions, like leading/trailing/center/bottom
DynamicIslandExpandedRegion(.leading) {
VStack {
Image(uiImage: UIImage(named: context.attributes.firstTeamLogo)!)
.resizable().frame(width: 70, height: 50)
Text(context.attributes.shortNameFirstTeam)
}
}
DynamicIslandExpandedRegion(.trailing) {
VStack {
Image(uiImage: UIImage(named: context.attributes.secondTeamLogo)!)
.resizable().frame(width: 70, height: 50)
Text(context.attributes.shortNameSecondTeam)
}
}
DynamicIslandExpandedRegion(.bottom) {
Text(context.state.information)
}
DynamicIslandExpandedRegion(.center) {
VStack {
Text("\(context.state.score1.description) - \(context.state.score2.description)")
.font(.largeTitle)
}
}
} compactLeading: {
HStack {
Text(context.attributes.shortNameFirstTeam)
Text(context.state.score1.description)
}
} compactTrailing: {
HStack {
Text(context.state.score2.description)
Text(context.attributes.shortNameSecondTeam)
}
} minimal: {
Image(systemName: "sportscourt.circle")
.foregroundColor(.indigo)
}
}
}
}

private struct LockScreenLiveActivityView: View {
let context: ActivityViewContext

var body: some View {
VStack(alignment: .leading) {
HStack {
VStack {
Image(uiImage: UIImage(named: context.attributes.firstTeamLogo)!)
.resizable()
.frame(width: 70, height: 50)
.cornerRadius(8)
Text(context.attributes.fullNameFirstTeam)
}
Spacer()
VStack {
Text("\(context.state.score1.description) - \(context.state.score2.description)")
.font(.largeTitle)
}
Spacer()
VStack {
Image(uiImage: UIImage(named: context.attributes.secondTeamLogo)!)
.resizable()
.frame(width: 70, height: 50)
.cornerRadius(8)
Text(context.attributes.fullNameSecondTeam)
}
}
Divider()
if context.state.information != "" {
HStack{
Image("ic_information")
.resizable()
.frame(width: 20, height: 20)
Text(context.state.information)
.multilineTextAlignment(.leading)
.font(.body.bold())
.lineLimit(1)
}.padding(.bottom, 8)
}
}.padding([.leading, .trailing, .top], 8)
}
}

Step 5

Starting an Activity

เราเรียกใช้งาน Activity ได้ด้วยเมธอดด้านล่างนี้

static func request(attributes: Attributes, contentState: Activity.ContentState, pushType: PushType? = nil) throws -> Activity

ในกรณีตัวอย่างที่ได้ยกขึ้นมา ผมต้องการที่จะติดตามการแข่งขันฟุตบอลที่กำลังแข่งขันอยู่ ดังนั้นจึงเรียกเปิดการใช้งาน Activity

private func startActivity() {
let attributes = FootballMatch(fullNameFirstTeam: "THAILAND", shortNameFirstTeam: "THAI", firstTeamLogo: "Thailand", fullNameSecondTeam: "JAPAN", shortNameSecondTeam: "JAP", secondTeamLogo: "Japan")
let contentState = FootballMatch.ContentState(score1: 0, score2: 0, information: "")
do {
currentActivity = try Activity.request(attributes: attributes, contentState: contentState)
} catch {
print(error.localizedDescription)
}
}

Updating an Activity

เราสามารถใช้เมธอดดังต่อไปนี้เพื่ออัปเดตข้อมูลให้กับ Activity

func update(using contentState: Activity.ContentState) async

ในแอปที่เราสร้าง จะมีการอัปเดตทุกๆครั้งที่มีข้อมูลสำคัญเกี่ยวการแข่งขันที่กำลังดำเนินการแข่งขันอยู่ ดังนั้นเราจะต้องส่งข้อมูลที่จะอัปเดตข้อมูลที่ใหม่เพื่อไปบอก Activity ดังนี้

private func updateActivity() {
let contentState = FootballMatch.ContentState(score1: 1, score2: 0, information: "Surasak get goal!!")
Task {
await currentActivity?.update(using: contentState)
}
}

💡ในการอัปเดตข้อมูลของ Activity เราสามารถส่งแจ้งเตือนด้วย AlertConfiguration ได้อีกด้วย

Ending an Activity

ถ้าเราต้องการที่จะยกเลิกการทำงานของ Activity เราสามารถใช้เมธอดนี้ในการยกเลิกการทำงาน

func end(using contentState: Activity.ContentState? = nil, dismissalPolicy: ActivityUIDismissalPolicy = .default) async

ในกรณีนี้ เราต้องการที่จะปิด Activity ที่แสดงของการแข่งขันที่เราเปิดไว้อยู่ เราจะใช้เมธอด end(using: , dismissalPolicy: )

private func stopActivity() {
let state = FootballMatch.ContentState(score1: 4, score2: 3, information: "Somsak got hat-trick!!")
Task {
await currentActivity?.end(using: state, dismissalPolicy: .af)
}
}

dismissalPolicy จะอยู่ด้วยกันทั้งหมด 3 ประเภท

immediate
รูปแบบการปิดของ immediate ก็คือระบบจะทำการลบ Activity ลงทันที

default
เราสามารถลบได้เอง หรือ ปล่อยทิ้งไว้เป็นเวลา 4 ชั่วโมงหลังจากที่สั่งปิดการทำงานของ Activity ลง เพื่อให้ระบบเป็นตัวลบ Activity ออกจากหน้าจอให้

after
เราสามารถกำหนดระยะเวลาในการหน่วงการลบ Activity ลงได้ หลังจากที่ทำการสั่งปิด Activity นั้นไปแล้ว

สามารถเข้าไปดูโค้ดได้ที่ด้านล่างนี้

Limitation

ไม่รองรับการดัดแปลงแอนิเมชันเมื่อทำการอัปเดต Live Activity

Live Activity อยู่ได้นานสูงสุด 12 ชั่วโมง

ไม่สามารถเข้าถึง network หรือการรับอัปเดตตำแหน่ง

ข้อมูลที่ใช้ในการอัปเดตต้องมีขนาดไม่เกิน 4kb

Reference

บทความล่าสุด
news
icon BTCicon ETH

ผู้ร่วมก่อตั้ง Empire Market ถูกตัดสินจำคุก 40 ปี

ศาลสหรัฐฯ สั่งจำคุก Raheim Hamilton ผู้ร่วมก่อตั้ง Empire Market เป็นเวลา 40 ปี ข้อหาเปิดเว็บมืดค้ายาเสพติดและสิ่งผิดกฎหมายมูลค่ากว่า 430 ล้านดอลลาร์

09 ต.ค. 69
AI Generated
0
icon source
icon BTCicon ETH|
09 ต.ค. 69 | AI Generated
news

อังกฤษเดินหน้าคว่ำบาตร 3 เว็บเทรดคริปโต เอี่ยวช่วยรัสเซียเลี่ยงมาตรการลงโทษ

สหราชอาณาจักรประกาศคว่ำบาตรเว็บเทรดคริปโต 3 แห่ง พร้อมผู้ให้บริการชำระเงินอีกหลายราย หลังสงสัยว่าช่วยรัสเซียหลบเลี่ยงมาตรการคว่ำบาตรจากชาติตะวันตก

09 ต.ค. 69
AI Generated
0
icon source
09 ต.ค. 69 | AI Generated
news
icon BTCicon USDCicon USDT

ฝรั่งเศสจ่อเก็บภาษีคริปโตใหม่ ในงบประมาณปี 2027

คณะกรรมาธิการการเงินของฝรั่งเศสหนุนข้อเสนอใหม่ เตรียมเก็บภาษีการสวอป Stablecoin และจัดหนักภาษีคนรวยที่ย้ายประเทศ โดยคิดจากกำไรคริปโตที่ยังไม่ได้ขาย

09 ต.ค. 69
AI Generated
0
icon source
icon BTCicon USDCicon USDT|
09 ต.ค. 69 | AI Generated