在剛剛結束的年度 iPhone 發佈活動上,Apple 在 iPhone 14 Pro 推出了「動態島」 (Dynamic Island),它是一個顯示新通知的創新方式。
我在 Xcode 14 release candidate 版本中體驗了這個新功能,希望在這篇文章中與大家分享。看完這篇文章,你就會發現我們可以簡單地在 SwiftUI 中複製這個功能。
當我們點擊按鈕時,動態島動畫就會啟動。我們會顯示動態島一段時間,然後再把它縮回原本的位置。
讓我們創建一個內容視圖,並添加一個按鈕,按鈕的操作就是發佈一個通知。
在此之前,我們需要先創建一個 NotificationModel
。
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundColor(.accentColor)
Text("Hello, Dynamic Island!")
Button("Notify Me 🥳") {
NotificationCenter.default.post(name: .init("NOTIFY"), object: NotificationModel(title: "Dynamic Island", content: "This is an example 😍"))
}
}
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct NotificationModel {
var title: String
var content: String
}
要在動態島上顯示視圖,我們需要忽略 safe area。
讓我們把這個視圖命名為 NotificationView
,因為它是一個靈活的視圖,因此我們會把它放在 GeometryReader
中,然後設置忽略 safe area。
@main
struct DynamicIslandExampleApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.overlay(alignment: .top) {
GeometryReader { proxy in
let size = proxy.size
NotificationView(size: size)
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
}
.ignoresSafeArea()
}
}
}
}
動態島有 11 像素的 top padding,其寬度為 126,高度為 37.33。
所以,我們要在 notch 後面放一個視圖,然後用動畫放大它。
因為這是一個範例,我們會維持視圖原有的 state,只改變它的尺寸。在收到通知時,我們就會為動畫化視圖及設置通知。
另外,我們會把 isExpanded
設置為 true。
struct NotificationView: View {
var size: CGSize
@State var isExpanded: Bool = false
@State var notification: NotificationModel?
var body: some View {
HStack {
// any view for notify
}
.frame(width: isExpanded ? size.width - 22 : 126, height: isExpanded ? 120 : 37.33)
.blur(radius: isExpanded ? 0 : 30)
.opacity(isExpanded ? 1 : 0)
.scaleEffect(isExpanded ? 1 : 0.5, anchor: .top)
.background {
RoundedRectangle(cornerRadius: isExpanded ? 50 : 63, style: .continuous)
.fill(.black)
}
.clipped()
.offset(y: 11)
.onReceive(NotificationCenter.default.publisher(for: .init("NOTIFY"))) { output in
guard let notification = output.object as? NotificationModel else { return }
self.notification = notification
withAnimation(.interactiveSpring(response: 0.7, dampingFraction: 0.7, blendDuration: 0.7)) {
isExpanded = true
DispatchQueue.main.asyncAfter(deadline: .now() + 2.2) {
withAnimation(.interactiveSpring(response: 0.7, dampingFraction: 0.7, blendDuration: 0.7)) {
isExpanded = false
self.notification = nil
}
}
}
}
}
}
當我們點擊按鈕時,就會看到動態島的動畫。
然後,我們就可以繪製想要的視圖。
我們可以隨時觸發及發送通知。
如果我們想確定設備有沒有動態島,就需要自己編寫一個擴充功能。
Apple 還沒有發佈這個擴充功能,我希望這個功能會在 iOS 16.1 中推出。
現在,我們可以把控件對照一下設備的名稱。
extension UIDevice {
static var hasDynamicIsland: Bool {
["iPhone 14 Pro", "iPhone 14 Pro Max"].contains(current.name)
}
}
動態島的動畫與普通視圖動畫似乎沒有什麼不同,我們已經完成實作了。
我們還可以自己設定想要的尺寸和外觀。對於使用者來說,在 App 中添加了動態島真的十分漂亮。