在 WWDC 2021,Apple 為 SwiftUI 框架添加了大量新功能,減輕開發者的工作。在 iOS 15 中,AsyncImage
絕對是其中一個值得探討的新視圖。如果你的 App 需要從遠程伺服器加載和顯示圖像,有了這個新視圖,你就不需要編寫自己的程式碼來處理非同步 (asynchronous) 下載。
AsyncImage
是一個內置視圖,用於非同步加載和顯示 Remote Image。我們只需要輸入圖像 URL,AsyncImage
就會抓取 Remote Image,並將其顯示在螢幕上。
在這篇教學文章中,我們會看看如何在 SwiftUI 專案中使用 AsyncImage
。要跟隨這篇教學的步驟實作,請使用 Xcode 13 和 iOS 15。
AsyncImage 的基本使用
使用 AsyncImage 最簡單的方法,就是如此指定圖像的 URL:
AsyncImage(url: URL(string: imageURL))
然後,AsyncImage 就會連接到你提供的 URL,並非同步地下載 Remote Image。如果圖像尚未準備好顯示,它會自動將佔位符 (placeholder) 呈現為灰色。圖像完全下載好後,AsyncImage
就會以其固有尺寸 (intrinsic size) 顯示圖像。
如果想調整圖像的尺寸,我們可以這樣傳遞比例數值 (scaling value) 給 scale
參數 (parameter):
AsyncImage(url: URL(string: imageURL), scale: 2.0)
比例數值大於 1.0 就會縮小圖像,相反,比例數值少於 1 就會放大圖像。
客製化圖像尺寸和佔位符
AsyncImage
也提供了另一個構造函數 (constructor),讓開發者可以進一步客製化圖像:
init<I, P>(url: URL?, scale: CGFloat, content: (Image) -> I, placeholder: () -> P)
我們可以使用上面的 init
初始化 AsyncImage
,來調整及縮放下載好的圖像。更重要的是,我們可以實作自己的佔位符。看看以下的範例程式碼片段:
AsyncImage(url: URL(string: imageURL)) { image in
image
.resizable()
.scaledToFill()
} placeholder: {
Color.purple.opacity(0.1)
}
.frame(width: 300, height: 500)
.cornerRadius(20)
在上面的程式碼中,AsyncImage
提供了下載好的圖像。然後,我們應用 resizable()
和 scaledToFill()
修飾符來調整圖像尺寸,並把 AsyncImage
視圖的尺寸限制為 300×500 points。
placeholder
參數讓我們可以創建自己的佔位符,來取代預設的佔位符。在以下範例中,我們把佔位符設置為淺紫色。
處理非同步操作的不同階段 (Phase)
如果你想更好地控制非同步下載操作,AsyncImage
視圖就提供了另一個構造函數:
init(url: URL?, scale: CGFloat, transaction: Transaction, content: (AsyncImagePhase) -> Content)
AsyncImagePhase
是一個列舉 (enum),用於追踪下載操作的當前階段。你可以針對每個階段提供詳細的實作,包括 empty、failure 和 success。
看看以下範例程式碼片段:
AsyncImage(url: URL(string: imageURL)) { phase in
switch phase {
case .empty:
Color.purple.opacity(0.1)
case .success(let image):
image
.resizable()
.scaledToFill()
case .failure(_):
Image(systemName: "exclamationmark.icloud")
.resizable()
.scaledToFit()
@unknown default:
Image(systemName: "exclamationmark.icloud")
}
}
.frame(width: 300, height: 300)
.cornerRadius(20)
在 Empty 的情況下,表示圖像未加載,於是我們會顯示一個佔位符。在 success 的情況下,我們就會應用幾個修飾符,並將圖像顯示在螢幕上。在 failure 的情況下,我們就可以在出現錯誤時提供備用視圖 (alternate view)。在上面的程式碼中,我們就這樣顯示了一個系統圖像。
利用 Transaction 來添加動畫
同一個 init
可以讓我們在階段更改時指定可選 transaction
。例如,以下程式碼片段在 transaction
參數中指定使用 spring 動畫:
AsyncImage(url: URL(string: imageURL), transaction: Transaction(animation: .spring())) { phase in
switch phase {
case .empty:
Color.purple.opacity(0.1)
case .success(let image):
image
.resizable()
.scaledToFill()
case .failure(_):
Image(systemName: "exclamationmark.icloud")
.resizable()
.scaledToFit()
@unknown default:
Image(systemName: "exclamationmark.icloud")
}
}
.frame(width: 300, height: 500)
.cornerRadius(20)
如此一來,在下載圖像後,我們就會看到淡入 (fade-in) 動畫。我們無法在預覽版面中測試程式碼,請在模擬器中測試程式碼以查看動畫。
你也可以把 transition
修飾符附加到 image
視圖:
case .success(let image):
image
.resizable()
.scaledToFill()
.transition(.slide)
如此一來,在顯示結果圖像時,就會看到滑入 (slide-in) 動畫。
原文:Using AsyncImage in SwiftUI for Loading Images Asynchronously