SwiftUI 新功能:利用AsyncImage 非同步加载和显示Remote Image

在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) 显示图像。

如果想调整图像的尺寸,我们可以这样传递比例数值(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),用于追踪下载操作的当前阶段。你可以针对每个阶段提供详细的实作,包括emptyfailuresuccess

看看以下范例程式码片段:

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) 动画。

庄朋龙
庄朋龙

一个爱生活的技术菜鸟

留下评论

您的邮箱地址不会被公开。 必填项已用 * 标注