简体   繁体   English

在UICollectionView和UITableView中最佳地预加载/预取图像

[英]Optimally preloading / prefetching images in UICollectionView and UITableView

I have what I would imagine is a very common situation that I expected to be easy to solve - an app with a UICollectionView, with an image in each cell that is fetched from an API on a web server via http/https. 我有一个我想象的非常常见的情况,我希望很容易解决 - 一个带有UICollectionView的应用程序,每个单元格中的图像通过http / https从Web服务器上的API获取。

I'm attempting to implement iOS 10's prefetching in UICollectionView , so that images are that are likely to be required are requested ahead of time. 我正在尝试在UICollectionView中实现iOS 10的预取 ,以便提前请求可能需要的图像。

It seems to me that there are various requirements that should be met for optimal prefetching: 在我看来,最佳预取应满足各种要求:

  1. Fetching of images that are actually immediately required should always be prioritised above speculative prefetching, even when the image is already in a queue to be fetched. 实际上立即需要的图像的获取应始终优先于推测预取,即使图像已经在要获取的队列中。
  2. When iOS tells us a particular image is no longer required to be prefetched, it (and only it) should be removed from the prefetch queue. 当iOS告诉我们不再需要预取特定图像时,应该从预取队列中删除它(并且只有它)。
  3. If an image is requested it should always be delivered unless /all/ requests for it are canceled (easy to get wrong if one image is used in multiple cells or multiple view controllers). 如果请求图像,则应始终传送图像,除非取消/全部/请求图像(如果在多个单元格或多个视图控制器中使用一个图像,则容易出错)。
  4. Prioritised fetching should be aborted if didEndDisplaying is called (but, if prefetching was requested and wasn't cancelled, the image should presumably be left in the queue at a lower priority). 如果调用didEndDisplaying,则应该中止优先获取(但是,如果请求了预取但未取消,则图像应该以较低的优先级留在队列中)。
  5. Various common requirements that simply relate to image loading - eg. 与图像加载有关的各种常见要求 - 例如。 don't flood the underlying system with prefetch requests that will end up taking bandwidth and server slots away from images that are required immediately. 不要使用预取请求充斥底层系统,这些请求最终会使带宽和服务器插槽远离立即需要的映像。

I've looked at various existing libraries like SDWebImage and Kingfisher , and was surprised that there doesn't appear to be any way to meet the above requirements easily with either library. 我查看了各种现有的库,如SDWebImageKingfisher ,并且很惊讶,似乎没有任何方法可以轻松地使用任何一个库来满足上述要求。

For example, SDWebImage doesn't meet the second requirement - you can only cancel all prefetching , or you must create a pre-fetcher per image (which then means various other SDWebImage features, like limiting the number of concurrent requests of pre-fetched images - ie. requirement 5 - no longer work.) 例如,SDWebImage不符合第二个要求 - 您只能取消所有预取 ,或者必须为每个图像创建一个预取程序(这意味着其他各种SDWebImage功能,例如限制预取图像的并发请求数量) - 即要求5 - 不再有效。)

Is this really a hard problem? 这真的是一个难题吗? Did I miss some obvious solution? 我错过了一些明显的解决方案吗 Am I overthinking the requirements? 我是否过度思考要求?

It seems you'll need to implement your own priority if the latest indexpath passed from prefetchItemsAt indexPaths doesn't suffice. 如果从prefetchItemsAt indexPaths传递的最新索引路径不够,您似乎需要实现自己的优先级。

Requirements 1, 2, 3 and be accomplished using a Priority Queue (Sorted Heap). 要求1,2,3使用优先级队列(排序堆)完成。 Requirement 4 depends on your implementation the Priority Queue. 要求4取决于您的优先级队列的实施。 5 is quite broad and vague, just limit the number of calls to your method for fetching. 5是相当宽泛和模糊的,只是限制了你的方法的提取次数。 The Priority Queue takes a generic type so you can use it with an object/class that has properties for the items you wish to fetch, below I just use IndexPath.row for simplicity. 优先级队列采用泛型类型,因此您可以将它与对象/类一起使用,该对象/类具有您要获取的项目的属性,下面我只使用IndexPath.row来简化。

Create a priority queue that will serve as a mediator between your datasource and the cache. 创建一个优先级队列,用作数据源和缓存之间的中介。

then set the data to fetch from your DataSource by peeking/dequeuing the priority queue and storing in Kingfisher. 然后通过查看/出列优先级队列并存储在Kingfisher中,将数据设置为从DataSource获取。 Your DataSource -> PriorityQueue -> Kingfisher -> Cell 您的DataSource - > PriorityQueue - > Kingfisher - > Cell

func collectionView(_ collectionView: UICollectionView, prefetchItemsAt indexPaths: [IndexPath]) {
     ...
    for indexPath in IndexPaths {
       //Add the indexPath to the top of the priority queue
       //highest priority is determined by the sort function you used when initializing the priority queue. 
       priorityQueue?.enqueue(indexpath.row)
    }

   //Call your method for getting the properties
   self.fetchObjectsByPriority() 
}


 func fetchObjectsByPriority() {
        if let count = priorityQueue?.count {
        for _ in 0...count {

            //get the index
            var index = self.priorityQueue?.dequeue()
            //Alternatively .peek() with out dequeuing item
            //get image data and store
            dataSource[index].image = ...

        }
    }
}

make sure you initialize the priority queue with a sort function. 确保使用sort函数初始化优先级队列。

you can get a prebuilt priority queue from here you will also need a heap to create the priority queue. 您可以从此处获得预构建的优先级队列,您还需要一个来创建优先级队列。

more on priority Queues and heap sorting 更多关于优先级队列堆排序

Without the code in question it is difficult to offer an effective solution. 如果没有相关代码,很难提供有效的解决方案。 I hope this helps, cheers and good luck! 我希望这会有所帮助,欢呼和祝你好运!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM