Last active
July 28, 2019 21:49
-
-
Save piyushdec/0e18c241d931e56a00d796974ebff211 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class MainViewController: UIViewController { | |
var imageRecords: [ImageRecord] = [] | |
let pendingOperations = PendingOperations() | |
} | |
extension MainViewController: UITableViewDataSource { | |
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { | |
let cell = tableView.dequeueReusableCell(withIdentifier: "identifier", for: indexPath) | |
let imageDetails = imageRecords[indexPath.row] | |
cell.textLabel?.text = imageDetails.name | |
cell.imageView?.image = imageDetails.image | |
switch imageDetails.state { | |
case .failed: | |
cell.textLabel?.text = "Failed to load" | |
case .new, .downloaded: | |
//You tell the table view to start operations only if the table view is not scrolling. | |
//These are actually properties of UIScrollView and, because UITableView is a subclass of UIScrollView, | |
//table views automatically inherit these properties. | |
if !tableView.isDragging && !tableView.isDecelerating { | |
startDownload(for: imageDetails, at: indexPath) | |
} | |
} | |
return cell | |
} | |
} | |
extension MainViewController { | |
func fetchImageDataSource() { | |
let request = URLRequest(url: "https://your-server-url-to-fetch-endpoints") | |
let task = URLSession(configuration: .default).dataTask(with: request) { data, response, error in | |
self.imageRecords.append(record0...<recordn) | |
DispatchQueue.main.async { | |
self.tableView.reloadData() | |
} | |
} | |
task.resume() | |
} | |
func startDownload(for imageRecord: ImageRecord, at indexPath: IndexPath) { | |
guard pendingOperations.downloadsInProgress[indexPath] == nil else { | |
return | |
} | |
let downloader = ImageDownloadHelper(imageRecord) | |
downloader.completionBlock = { | |
if downloader.isCancelled { | |
return | |
} | |
DispatchQueue.main.async { | |
self.pendingOperations.downloadsInProgress.removeValue(forKey: indexPath) | |
self.tableView.reloadRows(at: [indexPath], with: .auto) | |
} | |
} | |
pendingOperations.downloadsInProgress[indexPath] = downloader | |
pendingOperations.downloadQueue.addOperation(downloader) | |
} | |
} | |
extension MainViewController { | |
override func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { | |
suspendAllOperations() | |
} | |
override func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { | |
if !decelerate { | |
loadImagesForVisibleCells() | |
resumeAllOperations() | |
} | |
} | |
override func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { | |
loadImagesForVisibleCells() | |
resumeAllOperations() | |
} | |
} | |
extension MainViewController { | |
func suspendAllOperations() { | |
pendingOperations.downloadQueue.isSuspended = true | |
} | |
func resumeAllOperations() { | |
pendingOperations.downloadQueue.isSuspended = false | |
} | |
func loadImagesForVisibleCells() { | |
if let pathsArray = tableView.indexPathsForVisibleRows { | |
var allPendingOperations = Set(pendingOperations.downloadsInProgress.keys) | |
//get cancellable indexpaths by deleting all visible indexpaths | |
var toBeCancelledIndexPaths = allPendingOperations | |
let visibleIndexPaths = Set(pathsArray) | |
toBeCancelledIndexPaths.subtract(visibleIndexPaths) | |
//get resumable indexpaths by deleting all other indexpaths | |
var toBeStartedIbdexPaths = visibleIndexPaths | |
toBeStartedIbdexPaths.subtract(allPendingOperations) | |
//iterate all cancellable indexpaths and cancel them | |
for indexPath in toBeCancelledIndexPaths { | |
if let pendingDownload = pendingOperations.downloadsInProgress[indexPath] { | |
pendingDownload.cancel() | |
} | |
pendingOperations.downloadsInProgress.removeValue(forKey: indexPath) | |
} | |
//iterate all resumable indexpaths and start them again | |
for indexPath in toBeStartedIndexPaths { | |
let recordToProcess = imageRecords[indexPath.row] | |
startDownload(for: recordToProcess, at: indexPath) | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment