official Apple sample code 显示了一种您可能会发现有用的技术。无需依赖UICollectionView 来接收回调并根据需要加载数据,您可以观察滚动并根据contentOffset 的值,您将知道需要多少前瞻性。
在此处复制粘贴相关部分,以防将来的读者无法使用链接。所有这些都存在于UICollectionViewController 子类中——它可以适应任何UIScrollView。
func scrollViewDidScroll(_ scrollView: UIScrollView) {
// This is your trigger point
updateCachedAssets()
}
var fetchResult: PHFetchResult<PHAsset>!
fileprivate let imageManager = PHCachingImageManager()
fileprivate let thumbnailSize: CGSize = CGSize(width: 100, height: 100)
fileprivate var previousPreheatRect = CGRect.zero
fileprivate func updateCachedAssets() {
// Update only if the view is visible.
guard isViewLoaded && view.window != nil else { return }
// The window you prepare ahead of time is twice the height of the visible rect.
let visibleRect = CGRect(origin: collectionView!.contentOffset, size: collectionView!.bounds.size)
let preheatRect = visibleRect.insetBy(dx: 0, dy: -0.5 * visibleRect.height)
// Update only if the visible area is significantly different from the last preheated area.
let delta = abs(preheatRect.midY - previousPreheatRect.midY)
guard delta > view.bounds.height / 3 else { return }
// Compute the assets to start and stop caching.
let (addedRects, removedRects) = differencesBetweenRects(previousPreheatRect, preheatRect)
let addedAssets = addedRects
.flatMap { rect in collectionView!.indexPathsForElements(in: rect) }
.map { indexPath in fetchResult.object(at: indexPath.item) }
let removedAssets = removedRects
.flatMap { rect in collectionView!.indexPathsForElements(in: rect) }
.map { indexPath in fetchResult.object(at: indexPath.item) }
// Update the assets the PHCachingImageManager is caching.
imageManager.startCachingImages(for: addedAssets,
targetSize: thumbnailSize, contentMode: .aspectFill, options: nil)
imageManager.stopCachingImages(for: removedAssets,
targetSize: thumbnailSize, contentMode: .aspectFill, options: nil)
// Store the computed rectangle for future comparison.
previousPreheatRect = preheatRect
}
fileprivate func differencesBetweenRects(_ old: CGRect, _ new: CGRect) -> (added: [CGRect], removed: [CGRect]) {
if old.intersects(new) {
var added = [CGRect]()
if new.maxY > old.maxY {
added += [CGRect(x: new.origin.x, y: old.maxY,
width: new.width, height: new.maxY - old.maxY)]
}
if old.minY > new.minY {
added += [CGRect(x: new.origin.x, y: new.minY,
width: new.width, height: old.minY - new.minY)]
}
var removed = [CGRect]()
if new.maxY < old.maxY {
removed += [CGRect(x: new.origin.x, y: new.maxY,
width: new.width, height: old.maxY - new.maxY)]
}
if old.minY < new.minY {
removed += [CGRect(x: new.origin.x, y: old.minY,
width: new.width, height: new.minY - old.minY)]
}
return (added, removed)
} else {
return ([new], [old])
}
}
注意事项
- 该代码已有多年历史,您需要根据自己的要求对其进行调整。
- 您可以调整 preheatRect 计算以满足您的需求。
// The window you prepare ahead of time is twice the height of the visible rect.
let visibleRect = CGRect(origin: collectionView!.contentOffset, size: collectionView!.bounds.size)
let preheatRect = visibleRect.insetBy(dx: 0, dy: -0.5 * visibleRect.height)
- 示例实现此
preheatRect 逻辑以预加载PHAsset 缩略图。您需要将您的数据源调整为类似的样式并开始/停止加载您的资源。