我需要正确执行此操作,我认为这是常见的情况:
我有一个UITableViewController
,其表格的单元格应该下载图像并显示它。单元格数量取决于用户输入,我首先获取我需要下载的图像的URL,每个单元格一个。在这个UITableViewController
我有这个方法:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard results.count > 0 else {
return UITableViewCell()
}
let myCell = tableView.dequeueReusableCell(withIdentifier: CustomCell.cellIdentifier, for: indexPath) as! CustomCell
let result = results[indexPath.row]
myCell.model = result
myCell.imageProvider = imageProvider
return myCell
}
CustomCell
是这样的:
class CustomCell: UITableViewCell {
// Several IBOutlets
static let cellIdentifier = "myCell"
var imageProvider: ImageProvider?
var model: MyModel? {
willSet {
activityIndicator.startAnimating()
configureImage(showImage: false, showActivity: true)
}
didSet {
guard let modelUrlStr = model?.imageUrlStr, let imageUrl = URL(string: modelUrlStr) else {
activityIndicator.stopAnimating()
configureImage(showImage: false, showActivity: false)
return
}
imageProvider?.getImage(imageUrl: imageUrl, completion: {[weak self] (image, error) in
DispatchQueue.main.async {
guard error == nil else {
self?.activityIndicator.stopAnimating()
self?.configureImage(showImage: false, showActivity: false)
return
}
self?.imageView.image = image
self?.activityIndicator.stopAnimating()
self?.configureImage(showCoverImage: true, showActivity: false)
}
})
}
}
override func awakeFromNib() {
super.awakeFromNib()
configureImage(showCoverImage: false, showActivity: false)
}
override func prepareForReuse() {
super.prepareForReuse()
model = nil
}
private func configureImage(showImage: Bool, showActivity: Bool) {
// Update image view
}
}
ImageProvider
就是这样:
class ImageProvider {
var imageTask: URLSessionDownloadTask?
func getImage(imageUrl: URL, completion: @escaping DownloadResult) {
imageTask?.cancel()
imageTask = NetworkManager.sharedInstance.getImageInBackground(imageUrl: imageUrl, completion: { (image, error) -> Void in
if let error = error {
completion(nil, error)
} else if let image = image {
completion(image, nil)
} else {
completion(nil, nil)
}
})
}
}
我需要做的是:
然后,最后,这是NetworkManager
:
class NetworkManager: NSObject {
static let sharedInstance = NetworkManager()
fileprivate var defaultSession: URLSession
fileprivate var backgroundSession: URLSession?
fileprivate var completionHandlers = [URL : ImageResult]()
override init() {
let configuration = URLSessionConfiguration.default
defaultSession = URLSession(configuration: configuration)
super.init()
let backgroundConfiguration = URLSessionConfiguration.background(withIdentifier: "com.example.mysearch")
backgroundSession = URLSession(configuration: backgroundConfiguration, delegate: self, delegateQueue: nil)
}
func getImageInBackground(imageUrl url: URL, completion: ImageResult?) -> URLSessionDownloadTask? {
guard let backgroundSession = self.backgroundSession else {
return nil
}
completionHandlers[url] = completion
let request = URLRequest(url: url)
let task = backgroundSession.downloadTask(with: request)
task.resume()
return task
}
}
extension NetworkManager: URLSessionDownloadDelegate {
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
if let error = error, let url = task.originalRequest?.url, let completion = completionHandlers[url] {
completionHandlers[url] = nil
completion(nil, error)
}
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
if let data = try? Data(contentsOf: location), let image = UIImage(data: data), let request = downloadTask.originalRequest, let response = downloadTask.response {
let cachedResponse = CachedURLResponse(response: response, data: data)
self.defaultSession.configuration.urlCache?.storeCachedResponse(cachedResponse, for: request)
if let url = downloadTask.originalRequest?.url, let completion = completionHandlers[url] {
completionHandlers[url] = nil
completion(image, nil)
}
} else {
if let url = downloadTask.originalRequest?.url, let completion = completionHandlers[url] {
completionHandlers[url] = nil
completion(nil, error)
}
}
}
func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
if let appDelegate = UIApplication.shared.delegate as? AppDelegate, let completionHandler = appDelegate.backgroundSessionCompletionHandler {
appDelegate.backgroundSessionCompletionHandler = nil
completionHandler()
}
}
}
当我运行应用程序时,我发现首先没有下载大部分图像,并且调用urlSession(session:task:didCompleteWithError:)
时出现如下错误:
错误域= NSURLErrorDomain代码= -999“已取消”UserInfo = {NSErrorFailingURLStringKey = http://xxxxx.jpg,NSLocalizedDescription =已取消,NSErrorFailingURLKey = http://http://xxxxx.jpg}
此外,系统还会显示带有“已取消”文本的警报视图。另一方面,我不明白为什么,当我向上和向下滚动表格时,大部分图像都会被下载并显示出来。
我正在关注Ray Wenderlich网站课程中提供的类似示例,但我不知道为什么我的应用程序效果不佳。
我做错了什么?我需要你的帮助才能搞清楚。
编辑:我在自定义单元格中始终拥有ImageProvider
的方式更新了代码。这是CustomCell
类:
private let imageProvider = ImageProvider()
但我仍然得到相同的错误和“cabcelled”警报视图。
答案 0 :(得分:0)
由于您是在模型的didSet方法中启动图像下载,因此实习生需要imageProvider开始下载。您在设置imageProvider之前设置模型。
尝试更新以下代码,
Select ad_id, count(ad_id) as exposures from ad_history group by ad_id order by ad_id asc;
}