如何在 Swift 上使这段代码异步

Posted

技术标签:

【中文标题】如何在 Swift 上使这段代码异步【英文标题】:How to make this code asynchronous on Swift 【发布时间】:2020-03-08 15:51:10 【问题描述】:

我有一个包含照片图像 URL 的字符串数组。我有一个自定义布局,我需要将一个字符串数组转换为UIImage,然后获取它们的大小。 这是我的代码:

extension PhotoFeedViewController: CustomLayoutDelegate 

func collectionView(_ collectionView: UICollectionView, sizeOfPhotoAtIndexPath indexPath: IndexPath) -> CGSize 

    let bodyImage = UIImageView()
    let url = URL(string: collectionPhotos[indexPath.row])

    let data = try? Data(contentsOf: url!)
//        DispatchQueue.main.async 
        if let imageData = data 
            bodyImage.image = UIImage(data: imageData)
        
//        

    return bodyImage.image?.size ?? CGSize(width: 100, height: 100)
   

此代码有效,但存在问题。当我启动应用程序时,图像会在所有图像加载后出现,因为一切都是同步的。

如果您取消注释:

DispatchQueue.main.async 

返回的属性为空,看不到 Dispatch 块中的代码

如何使此代码异步,以便异步传递图像的大小并且用户不等待图像的全部加载显示在屏幕上?不使用库。

对不起我的英语。

【问题讨论】:

这能回答你的问题吗? Loading/Downloading image from URL on Swift 所有图片都使用固定尺寸会更好。另一种选择是构造您的数据,将图像大小添加到它和图像 url,这样您无需等待下载图像数据即可知道其大小。 @dahiya_boy - 这些答案中的大多数已经过时和/或提倡次优模式。此外,这个问题还有一个复杂的层次(即 Nikita 正在下载图像只是为了计算大小,而不是保存下载的图像,没有考虑到图像之前可能已经下载过,等等)。 【参考方案1】:

你说的对,同步请求是个问题。 (而且我们经常使用URLSession,而不仅仅是将其包装在async 调用中。)

但您可能必须从根本上重新考虑这个过程。您真的不想下载图像以便确定它的大小,以便可以在cellForItemAt 重新下载它再次。 (而且我不建议依靠 URLCache 来拯救你。)

理想情况下,如果这个 URL 数组的源可以同时返回图像的大小,就可以快刀斩乱麻。如果您有一个 URL 列表和相应的图像大小,那将消除下面讨论的所有问题。

如果做不到这一点,您将面临两个基本选择:

    您是否只想强制所有图像在 UI 中的大小相同,而不管实际图像的大小(例如,使用“纵横比填充”内容模式)?那将是最简单的。

    您是否要在下载图像之前假设某个标准尺寸,然后在相应图像下载完成时重新加载该特定单元格(可能导致所有内容重新布局)?由于您无法始终控制异步图像的返回顺序,因此有时这会导致集合视图中出现不和谐的变化。

后一种方法有很多小问题:

编写一个下载管理器来跟踪下载的图像,缓存以前的下载(理想情况下,在内存和持久存储中)。通过使用缓存,如果您将单元格滚动到视图中,它将检查您是否已经下载了图像,如果是,请使用它。例如。当您在集合视图中向上滚动时,您不希望针对之前下载的图像发起网络请求。

在异步图像检索过程中,您不仅要更新图像视图,还要完全重新加载与该单元格的 IndexPath 关联的单元格。这将触发它再次查询图像的大小。

所以“此单元格的大小”例程实际上会说“我下载了图像吗?如果是,则返回其大小;如果不启动异步下载(完成后将重新加载单元格)并返回一些占位符大小。” cellForItemAt会有类似的逻辑。

其他一些注意事项:

您正在返回图像的大小。我不知道您是如何使用这种方法的,但我们通常会在根据图像大小(通常以像素为单位)确定图像视图的大小(以点为单位)之前考虑设备的规模。

如果您还没有这样做,当您解决上述问题时,您可能希望实现预取。如果在您滚动到集合视图中的特定内容偏移时已经下载了图像,这将减少任何不和谐的单元格大小调整。

【讨论】:

以上是关于如何在 Swift 上使这段代码异步的主要内容,如果未能解决你的问题,请参考以下文章

如何使这段代码更短[关闭]

如何使这段代码简短易读?

如何使用 for 循环使这段代码更短

在 Swift 中向用户显示网络错误消息

入口点不能用“异步”修饰符标记

如何将异步Func或Action转换为委托并调用它