问题描述
我使用 URLSessionDataTask
显示图像,并使用 URLSessionDownloadTask
在我的应用中下载图像。我还需要观察下载进度,所以我在 Progress
的 fractionCompleted
使用 KVO。
问题是 fractionCompleted
值只有 0.05 和 1.0。我认为这个问题发生在使用相同的远程 url 时。因为当我不使用 URLSessionDataTask
时(虽然我无法显示图像),我会得到正确的进度更新。
为什么会这样?有参考吗?
已编辑
首先,我不想使用 URLSession
委托来获取进度。我正在使用两个 VC(第一个是 RemoteGridViewController
,使用 URLSessionDataTask
显示图像,第二个是 MediaViewerController
)。 MediaViewerController
委托第一个 VC 下载相机胶卷上的图像。我知道我可以使用 URLSession
委托方法通过使用 NotificationCenter
或类似的东西来通知进度,但我不想要。这种方式在 VC 之间形成了强耦合(?)(我猜...)
其次,RemoteGridViewControlle
r 使用 URLSession
显示 URLSessionDataTask
的图像并缓存它。
guard let url = URL(string: url) else {
completionHandler(.failure(NetworkManagerError.urlError))
return
}
var req = URLRequest(url: url)
req.cachePolicy = .returnCacheDataElseLoad
if let cachedResponse = sharedCache?.cachedResponse(for: req) {
completionHandler(.success(cachedResponse.data))
} else {
sessionDataTask = session.dataTask(with: req) { [weak self] (data,response,error) in
if let error = error {
completionHandler(.failure(error))
print(error.localizedDescription)
return
}
guard let response = response as? HTTPURLResponse,let data = data else {
completionHandler(.failure(NetworkManagerError.dataError))
return
}
self?.sharedCache?.storeCachedResponse(CachedURLResponse(response: response,data: data),for: req)
completionHandler(.success(data))
}
sessionDataTask?.resume()
}
第三,MediaViewerController
调用MediaViewerDelegate
方法,即downloadImage(itemAt:for:)
下载图片。
extension RemoteGridViewController: MediaViewerDelegate {
func downloadImage(itemAt indexPath: IndexPath,for mediaViewer: MediaViewerController) {
let data = unsplashData[indexPath.item]
let urlString = data.regular
guard let url = URL(string: urlString) else { return }
mediaViewer.presentProgressView()
networkManager.downloadTaskForImage(with: url,progressHandler: mediaViewer.observe(_:)) { result in
switch result {
case .success(let image):
dispatchQueue.main.async {
mediaViewer.toastView(with: true)
}
UIImageWritetoSavedPhotosAlbum(image,nil,nil)
case .failure(let error):
dispatchQueue.main.async {
mediaViewer.toastView(with: false)
}
print(error.localizedDescription)
}
}
}
}
class NetworkManager {
private let session: URLSession!
private var sessionDownloadTask: URLSessionDownloadTask?
func downloadTaskForImage(with url: URL,progressHandler: (Progress?) -> (),completionHandler: @escaping (Result<UIImage,Error>) -> ()) {
sessionDownloadTask = session.downloadTask(with: url) { (location,error) in
if let error = error {
completionHandler(.failure(error))
return
}
guard let response = response as? HTTPURLResponse,(200...299).contains(response.statusCode) else {
completionHandler(.failure(NetworkManagerError.responseError))
return
}
guard let location = location else {
completionHandler(.failure(NetworkManagerError.urlError))
return
}
do {
let data = try Data(contentsOf: location)
if let image = UIImage(data: data) {
completionHandler(.success(image))
} else {
completionHandler(.failure(NetworkManagerError.dataError))
}
} catch let error {
completionHandler(.failure(error))
}
}
progressHandler(sessionDownloadTask?.progress)
sessionDownloadTask?.resume()
}
}
解决方法
您可以跟踪下载的字节数到总预期字节数以跟踪下载图像的进度。
使用以下委托方法:
URLSession(_:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:)
以及以下代码段:
func URLSession(session: NSURLSession,downloadTask: NSURLSessionDownloadTask,didWriteData bytesWritten: Int64,totalBytesWritten: Int64,totalBytesExpectedToWrite: Int64) {
// println("download task did write data")
let progress = Float(totalBytesWritten) / Float(totalBytesExpectedToWrite)
//Your code to download and save image to camera role
}
}
我希望这可以解决您的查询以检查下载的确切进度。