SwiftUI中AsyncImage的使用(一个高效的异步下载图片组件)

iOS开发者经常会遇到需要在应用中显示网络图像的场景,无论是获取和显示用户头像,展示产品图像,等等。在原来的UIKit中,如果我们要用系统的API还是稍微有点麻烦,很多开发的朋友都选择了第三方的框架去处理网络图片的请求缓存等等。

AsyncImageSwiftUI中一个强大的功能,它简化了在应用程序中异步加载和显示网络图像的过程。使用AsyncImage,可以无缝地处理图像加载,缺省视图,甚至错误处理,同时保持响应式用户界面。

不过这个组件需要iOS 15.0tvOS 15.0及以上的版本才能使用。

AsyncImage基本使用

AsyncImage使用共享的URLSession实例从指定的URL加载图像,然后显示它。例如:

在这里插入图片描述
AsyncImage最简单的使用方法就是只传入一个url,便会异步去加载这张图片,在图片加载出来之前,整个组件背景色为淡灰色,也可以认为缺省图就是一个淡灰色的背景。

我们也可以对AsyncImage添加很多的修饰符,比如backgroundcornerRadius等等,如果修改了background,那么在图片加载出来之前显示的就是这个设置的background
在这里插入图片描述

struct AsyncImageDemo: View {
  private let photoURL = URL(string: "https://picsum.photos/200")

  var body: some View {
    AsyncImage(url: photoURL)
      .frame(width: 200, height: 200)
      .background(Color.red)
      .cornerRadius(20)
      .shadow(color: .black.opacity(0.6) ,radius: 20, y: 10)
  }
}

placeholder

很多的时候我们都会设置图片在未加载完的显示内容,这不仅限于一个缺省的默认颜色,在加载图片时显示缺省视图可以显著增强用户体验。特别是在网络图像可能需要一些时间来获取的情况下。

通过下面的初始化方法,我们可以自定义缺省图。如果在加载时发生错误,将显示相同的缺省图。
在这里插入图片描述

struct AsyncImageDemo: View {
  private let photoURL = URL(string: "https://picsum.photos/200")

  var body: some View {
    AsyncImage(url: photoURL) { image in
      image
        .resizable()
        .background(Color.red)
        .cornerRadius(20)
        .shadow(color: .black.opacity(0.6) ,radius: 20, y: 10)
    } placeholder: {
      ProgressView()
        .progressViewStyle(.circular)
    }
    .frame(width: 200, height: 200)
  }
}

上面初始化方法里,第一个闭包返回了一个Image实例,我们可以对这个Image添加很多的修饰符。
第二个闭包里面就可以设置缺省图,这里显示了一个ProgressView组件,也可以设置为一个颜色组件,那就和基础用法里的一样了。
另外添加frame修饰符的时候,可以添加给Image,也可以添加给AsyncImage,区别就在于修饰的范围大小,添加给AsyncImage,那么ProgressView也是该尺寸。

处理请求error

如果想要获得对加载过程的更多控制,可以使用init(url:scale:transaction:content:)初始化方法,它接受一个内容闭包,该闭包返回一个AsyncImagePhase来指示加载操作的状态,返回一个适合当前阶段的视图:

AsyncImagePhase是一个枚举,它代表了图片加载的每个阶段:

@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
public enum AsyncImagePhase : Sendable {

    /// No image is loaded.
    case empty

    /// An image succesfully loaded.
    case success(Image)

    /// An image failed to load with an error.
    case failure(any Error)

    /// The loaded image, if any.
    ///
    /// If this value isn't `nil`, the image load operation has finished,
    /// and you can use the image to update the view. You can use the image
    /// directly, or you can modify it in some way. For example, you can add
    /// a ``Image/resizable(capInsets:resizingMode:)`` modifier to make the
    /// image resizable.
    public var image: Image? { get }

    /// The error that occurred when attempting to load an image, if any.
    public var error: (any Error)? { get }
}

在这里插入图片描述

AsyncImage(url: photoURL, transaction: Transaction(animation: .easeInOut)) { phase in
  switch phase {
    case .empty:
      ProgressView()
        .progressViewStyle(.circular)
    case .success(let image):
      image
        .resizable()
        .transition(.scale(scale: 0.1, anchor: .center))
    case .failure:
      Image(systemName: "questionmark.diamond")
        .imageScale(.large)
    @unknown default:
        EmptyView()
    }
}
.frame(width: 200, height: 200)

在闭包内,通过switch判断来决定每个阶段要显示的内容。

该初始化方法中还传入了transaction参数,它需要传入一个Transaction实例,用来决定AsyncImagePhase每个阶段的转场动画。

除了用switch判断每个阶段,也可以直接判断是否得到了图片或者error等,比如下面的代码:

struct AsyncImageDemo: View {
  private let photoURL = URL(string: "https://picsum.photos/200")

  var body: some View {
    AsyncImage(url: photoURL) { phase in
      if let image = phase.image {
        // Displays the loaded image.
        image
          .resizable()
          .cornerRadius(20)
          .shadow(color: .black.opacity(0.6) ,radius: 20, y: 10)
      } else if phase.error != nil {
        // Displays error view
        Image(systemName: "questionmark.diamond")
          .imageScale(.large)
      } else {
        // Displays loading view
        ProgressView()
          .progressViewStyle(.circular)
      }
    }
    .frame(width: 200, height: 200)
  }
}

写在最后

本篇文章主要介绍了如何使用AsyncImage视图下载和显示网络图像,这个组件大大的简化了我们加载网络图片的代码和相关的逻辑。

最后,希望能够帮助到有需要的朋友,如果您觉得有帮助,还望点个赞,添加个关注,笔者也会不断地努力,写出更多更好用的文章。

  • 31
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值