滚动视图轻拂(结束拖拽)时确定目标IndexPath
精准实现轻拂滚动后仅预加载目标单元格图片
嘿,你找对方向了!针对统一单元格尺寸的无限滚动TableView,用scrollViewWillEndDragging来捕获最终停留位置,只预加载对应图片的思路特别高效,刚好能解决你的需求。下面是完整的落地方案:
核心逻辑拆解
因为所有单元格尺寸一致,我们可以直接通过系统给出的targetContentOffset计算出滚动结束后最终停留在视野内的单元格索引。这样就能精准只加载这一个单元格的图片,同时取消之前的加载任务,避免不必要的资源消耗。
完整代码实现
首先,我们需要在ViewController里维护一个加载任务的引用,用来取消未完成的旧任务,然后补全你的scrollViewWillEndDragging方法:
// 用来跟踪当前的图片加载任务,方便取消之前的请求 private var ongoingImageLoadTask: URLSessionDataTask? func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) { guard let tableView = scrollView as? UITableView else { return } // 1. 获取统一的单元格高度(如果是固定值可以直接写死,比如200) let cellHeight = tableView.rowHeight // 要是你用了automaticDimension,记得提前存好实际固定高度,比如: // let cellHeight: CGFloat = 200 // 替换成你的单元格真实高度 // 2. 拿到滚动结束后的目标Y偏移量 let targetVerticalOffset = targetContentOffset.pointee.y // 3. 计算目标单元格的索引(这里取的是滚动结束后顶部第一个可见cell) let targetRow = Int((targetVerticalOffset + tableView.contentInset.top) / cellHeight) let targetIndexPath = IndexPath(row: targetRow, section: 0) // 假设只有一个section // 4. 边界检查(无限滚动的话,根据你的数据源逻辑调整,比如不能小于0) guard targetRow >= 0 else { return } // 5. 先取消之前的加载任务,避免浪费流量和内存 ongoingImageLoadTask?.cancel() // 6. 开始预加载目标cell的图片 // 替换成你从数据源获取图片URL的逻辑 guard let imageUrl = yourDataSource[targetIndexPath.row].imageURL else { return } ongoingImageLoadTask = URLSession.shared.dataTask(with: imageUrl) { [weak self] data, _, error in guard let self = self, let imageData = data, error == nil else { return } let loadedImage = UIImage(data: imageData) // 回到主线程处理缓存和UI更新 DispatchQueue.main.async { // 这里可以把图片缓存到你的数据源,或者用第三方缓存库(比如Kingfisher/SDWebImage) // 举个例子,用Kingfisher的话直接调用预加载: // ImagePrefetcher(urls: [imageUrl]).start() // 手动缓存到数据源的话: self.yourDataSource[targetIndexPath.row].cachedImage = loadedImage // 如果目标cell此时已经在屏幕上,直接更新它的图片 if let targetCell = tableView.cellForRow(at: targetIndexPath) as? YourCustomCell { targetCell.cellImageView.image = loadedImage } } } ongoingImageLoadTask?.resume() }
额外优化小贴士
- 加载居中单元格:如果你想预加载滚动结束后屏幕居中的单元格,把索引计算改成这样:
let visibleCenterY = targetVerticalOffset + tableView.bounds.midY - tableView.contentInset.top let targetRow = Int(visibleCenterY / cellHeight) - 用第三方缓存库更省心:建议用Kingfisher或者SDWebImage这类成熟库,它们自带预加载、缓存过期、失败重试等功能,你只需要调用对应的预加载方法就行,不用手动管理URLSession任务。
- 无限滚动边界处理:如果是无限滚动,记得加判断确保
targetRow在你允许的加载范围内(比如不能超过当前预加载的数据源上限),避免越界崩溃。
内容的提问来源于stack exchange,提问作者suyashKD
相关产品推荐
相关产品推荐

