在WWDC 2021,Apple 为SwiftUI 框架添加了大量新功能,减轻开发者的工作。在iOS 15中,AsyncImage
绝对是其中一个值得探讨的新视图。如果你的App 需要从远程伺服器加载和显示图像,有了这个新视图,你就不需要编写自己的程式码来处理非同步(asynchronous) 下载。
AsyncImage
是一个内置视图,用于非同步加载和显示Remote Image。我们只需要输入图像URL,AsyncImage
就会抓取Remote Image,并将其显示在萤幕上。
注意:请使用Xcode 13 和iOS 15。
AsyncImage 的基本使用
使用AsyncImage 最简单的方法,就是如此指定图像的URL:
AsyncImage(url: URL(string: imageURL))
然后,AsyncImage 就会连接到你提供的URL,并非同步地下载Remote Image。如果图像尚未准备好显示,它会自动将占位符(placeholder) 呈现为灰色。图像完全下载好后,AsyncImage
就会以其固有尺寸(intrinsic size) 显示图像。
![](https://www.zhuangpenglong.com/wp-content/uploads/2022/11/asyncimage-placeholder-1024x488.png)
如果想调整图像的尺寸,我们可以这样传递比例数值(scaling value) 给 scale
参数(parameter):
AsyncImage(url: URL(string: imageURL), scale: 2.0)
比例数值大于1.0 就会缩小图像,相反,比例数值少于1 就会放大图像。
![](https://www.zhuangpenglong.com/wp-content/uploads/2022/11/asyncimage-scale-1024x489.png)
客制化图像尺寸和占位符
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
参数让我们可以创建自己的占位符,来取代预设的占位符。在以下范例中,我们把占位符设置为浅紫色。
![](https://www.zhuangpenglong.com/wp-content/uploads/2022/11/asyncimage-change-image-size-1024x488.png)
处理非同步操作的不同阶段(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)。在上面的程式码中,我们就这样显示了一个系统图像。
![](https://www.zhuangpenglong.com/wp-content/uploads/2022/11/asyncimage-phase-1024x488.png)
利用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) 动画。
![](https://www.zhuangpenglong.com/wp-content/uploads/2022/11/asyncimage-slide-animation-1024x569.gif)