[iOS] 16.1 Live Activities (即時動態) 快速上手教學 (下)

iOS 16.1 Live Activities(即時動態) 之動態島

上一篇我們提到了即時動態的基本應用和相關的設定,那這篇我們就來聊聊更多關於即時動態能做到的酷炫功能吧!

上次我們提到關於 ActivityConfiguration 的設置:

import SwiftUI
import WidgetKit

@available(iOSApplicationExtension 16.1, *)
@main
struct PizzaDeliveryWidget: Widget {
    var body: some WidgetConfiguration {
        ActivityConfiguration(for: PizzaDeliveryAttributes.self) { context in
            // 製作鎖定畫面應該顯示的畫面,這也同時支援沒有動態島的iOS裝置。
        } dynamicIsland: { context in
            // 製作動態島顯示畫面,這部分下一章會再細說
            // ...
        }
    }
} 

但也只有針對上半部 context 的設計去作延伸,但我相新眼尖的各位一定有發現在 closure 的下面有另外一個 context 的設定,看字面上的意思,當然就是關於動態島的設置,因為這部分的內容偏多、複雜,所以我就另外寫一篇文章來跟大家分享。

Dynamic Island 動態島

首先呢,以開發者的角度來看的話,Apple 官方在開發動態島的元件 UI 時,有列出了一些規範和不同的顯示狀況:

一. 當使用者只有一個顯示中的即時動態時,動態島會在畫面上呈現 Leading side (頭) 跟 Trailing side (尾)。

這時就只有兩側的畫面能夠顯示相關資訊,因此會建議開發者在這樣的規範下設置首要的資訊就好,以上禮拜提到的外送來繼續舉例的話,首要的資訊就是餐點的即時動態。

二. 當使用者同時有不只一個即時動態在運作的時候,兩個即時動態會以 minimal 的方式在 UI 上做互動,動態島選擇一個優先級別更高的顯示在 Leading side,而另一個會以圓形圖示展現在 trailing side 的位置。

三. 
若使用者長按查看某個即時動態的詳細資訊時,這時動態島會擴張成 expanded region ,其中又再細分成不同的約束( constraint )去做設定。

  • Center:位於相機組件下方,由於區域不大,所以建議放置 static data(靜態 data)。
  • Leading:為左半邊 L 型區塊,範圍較大能夠放圖像或是比較明顯的資訊。
  • Trailing:為右半邊反 L 型區塊,跟 Leading 的可用範圍基本一樣。
  • Bottom:下方到底的區塊,這邊的範圍比較規矩,因此能過做更多動態的調整,不管是 animation 或是動態文字都是不錯的選擇。
但如果你不需要這麼多區塊怎麼辦呢?
有些人可能只想要 Leading + Trailing 的話,這樣 Center 跟 Bottom 不就會卡在那個地方影響整體畫面嗎?

這個問題 Apple 官方也給出的相對應的方法去達到不同的要求,init(_:priority:content:) 能夠調整整個動態島在擴張之後區塊渲染的先後順序,因此就能達到特定區塊長滿的需求:




如此一來,開發者就有更多可以應用的空間,不管今天是怎樣的互動性 App 都能依照自己的風格去定義屬於自己的即時動態。

程式碼實作

照著上次建立過的 ActivityConfiguration 繼續往下擴展的話:

import SwiftUI
import WidgetKit

@available(iOSApplicationExtension 16.1, *)
@main
struct PizzaDeliveryWidget: Widget {
    var body: some WidgetConfiguration {
        ActivityConfiguration(for: PizzaDeliveryAttributes.self) { context in
            // 製作鎖定畫面應該顯示的畫面,這也同時支援沒有動態島的iOS裝置。
        } dynamicIsland: { context in
            // 製作動態島應該顯示的畫面
            DynamicIsland {
                //MARK: -文章上方提到的(三).
                DynamicIslandExpandedRegion(.leading) {
                    Label("(context.attributes.numberOfPizzas) Pizzas", systemImage: "bag")
                        .foregroundColor(.indigo)
                        .font(.title2)
                }
                
                DynamicIslandExpandedRegion(.trailing) {
                    Label {
                        Text(timerInterval: context.state.deliveryTimer, countsDown: true)
                            .multilineTextAlignment(.trailing)
                            .frame(width: 50)
                            .monospacedDigit()
                    } icon: {
                        Image(systemName: "timer")
                            .foregroundColor(.indigo)
                    }
                    .font(.title2)
                }
                
                DynamicIslandExpandedRegion(.center) {
                    Text("(context.state.driverName) is on their way!")
                        .lineLimit(1)
                        .font(.caption)
                }
                
                DynamicIslandExpandedRegion(.bottom) {
                    Button {
                        // Deep link into your app.
                    } label: {
                        Label("Call driver", systemImage: "phone")
                    }
                    .foregroundColor(.indigo)
                }
            //MARK: -文章提到的(一)
            } compactLeading: {
                // Create the compact leading presentation.
                // ...
            } compactTrailing: {
                // Create the compact trailing presentation.
                // ...
            //MARK: -文章提到的(二)
            } minimal: {
                // Create the minimal presentation.
                // ...
            }
            .keylineTint(.yellow)
        }
    }
} 
以官方範例來操作的話,可以看到我們文章提到的一、二、三都有被實作在裡面,每個區域都一一列出,但如果有些區域不設定的話,他就不會有任何操作反應。

如果 build & run 之後,應該可以看到在你的動態島被 request 完建立後,在有動態島的機子上就會看到你的動態島會有下面的幾種畫面:

那如果需要設定 priority 的話,假設你只要 Leading 跟 Trailing,則 Center 跟 Bottom 就不需要設定:

import SwiftUI
import WidgetKit

@available(iOSApplicationExtension 16.1, *)
@main
struct PizzaDeliveryWidget: Widget {
    var body: some WidgetConfiguration {
        ActivityConfiguration(for: PizzaDeliveryAttributes.self) { context in
            // 製作鎖定畫面應該顯示的畫面,這也同時支援沒有動態島的iOS裝置。
        } dynamicIsland: { context in
            // 製作動態島應該顯示的畫面
            DynamicIsland {
                //MARK: -文章上方提到的(三).
                DynamicIslandExpandedRegion(.leading, priority: 1) {
                    //自訂畫面
                }
            //MARK: -文章提到的(一)
            } compactLeading: {
                // Create the compact leading presentation.
                // ...
            } compactTrailing: {
                // Create the compact trailing presentation.
                // ...
            //MARK: -文章提到的(二)
            } minimal: {
                // Create the minimal presentation.
                // ...
            }
            .keylineTint(.yellow)
        }
    }
} 
應該有人發現在 Dynamic Island closure的下方有一行.keylineTint(.yellow)這行的作用就是可以決定動態島的邊框顏色,沒錯!這個設置真的很棒,這樣就可以依照每個 App 自己的主題色去做設置!

這樣基本上你就完成一個有動態島支援的即時動態啦!

以上就是這篇文章的內容,那因為公司隱私的問題,我還是沒辦法把自己實作的程式碼放上來,還請大家多多包涵,如果有任何問題歡迎到我的IG私訊我,或在底下留言告訴我喔~謝謝!



如果想給我一些支持,也歡迎買杯咖啡給我,謝謝大家!

留言

這個網誌中的熱門文章

[iOS] 16.1 Live Activities (即時動態) 快速上手教學 (上)

[iOS][UIKit] 實作 VisionKit 跟 Vision 來掃描圖片跟識別文字