在 Swift 上从 URL 加载/下载图像
Posted
技术标签:
【中文标题】在 Swift 上从 URL 加载/下载图像【英文标题】:Loading/Downloading image from URL on Swift 【发布时间】:2014-06-15 16:27:05 【问题描述】:我想从我的应用程序中的 URL 加载图像,所以我首先尝试使用 Objective-C,它可以工作,但是,使用 Swift,我有一个编译错误:
'imageWithData'不可用:使用对象构造'UIImage(data:)'
我的功能:
@IBOutlet var imageView : UIImageView
override func viewDidLoad()
super.viewDidLoad()
var url:NSURL = NSURL.URLWithString("http://myURL/ios8.png")
var data:NSData = NSData.dataWithContentsOfURL(url, options: nil, error: nil)
imageView.image = UIImage.imageWithData(data)// Error here
在 Objective-C 中:
- (void)viewDidLoad
[super viewDidLoad];
NSURL *url = [NSURL URLWithString:(@"http://myURL/ios8.png")];
NSData *data = [NSData dataWithContentsOfURL:url];
_imageView.image = [UIImage imageWithData: data];
_labelURL.text = @"http://www.quentinroussat.fr/assets/img/iOS%20icon's%20Style/ios8.png";
谁能解释一下为什么imageWithData:
不适用于 Swift,我该如何解决这个问题。
【问题讨论】:
试试这个imageURL.image = UIImage(data: myDataVar)
完美!谢谢 但是我不明白为什么这个方法在 Objective C 中起作用,而不是在 Swift 中……奇怪
当你在使用 Cocoa 类时遇到问题,尝试 CMD+单击类名,你应该可以看到该类的 Swift 界面!
if let url = NSURL(string: "imageurl") if let data = NSData(contentsOfURL: url) imageView.image = UIImage(data: data)
@LeoDabus 没关系。据我了解,“swift”标签将始终引用当前版本。指定版本的标签用于与版本特定语言功能相关的问题。这不是确定的,但请查看this meta post。
【参考方案1】:
Xcode 8 或更高版本 • Swift 3 或更高版本
同步:
if let filePath = Bundle.main.path(forResource: "imageName", ofType: "jpg"), let image = UIImage(contentsOfFile: filePath)
imageView.contentMode = .scaleAspectFit
imageView.image = image
异步:
创建一个带有完成处理程序的方法以从您的 url 获取图像数据
func getData(from url: URL, completion: @escaping (Data?, URLResponse?, Error?) -> ())
URLSession.shared.dataTask(with: url, completionHandler: completion).resume()
创建下载图片的方法(启动任务)
func downloadImage(from url: URL)
print("Download Started")
getData(from: url) data, response, error in
guard let data = data, error == nil else return
print(response?.suggestedFilename ?? url.lastPathComponent)
print("Download Finished")
// always update the UI from the main thread
DispatchQueue.main.async() [weak self] in
self?.imageView.image = UIImage(data: data)
用法:
override func viewDidLoad()
super.viewDidLoad()
print("Begin of code")
let url = URL(string: "https://cdn.arstechnica.net/wp-content/uploads/2018/06/macOS-Mojave-Dynamic-Wallpaper-transition.jpg")!
downloadImage(from: url)
print("End of code. The image will continue downloading in the background and it will be loaded when it ends.")
扩展:
extension UIImageView
func downloaded(from url: URL, contentMode mode: ContentMode = .scaleAspectFit)
contentMode = mode
URLSession.shared.dataTask(with: url) data, response, error in
guard
let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200,
let mimeType = response?.mimeType, mimeType.hasPrefix("image"),
let data = data, error == nil,
let image = UIImage(data: data)
else return
DispatchQueue.main.async() [weak self] in
self?.image = image
.resume()
func downloaded(from link: String, contentMode mode: ContentMode = .scaleAspectFit)
guard let url = URL(string: link) else return
downloaded(from: url, contentMode: mode)
用法:
imageView.downloaded(from: "https://cdn.arstechnica.net/wp-content/uploads/2018/06/macOS-Mojave-Dynamic-Wallpaper-transition.jpg")
【讨论】:
这里只是一个旁注,你应该给它设置一个关联的对象;否则,您可能会在彼此之上加载图像。例如。在UITableView
中,其中一个单元格显示图像,并且在返回出列单元格时更新图像。如果进程#1 比进程#2 花费更长的时间,进程#2 将显示其图像,然后进程#1 将更新它,即使该图像对用户不再有效。
@PaulPeelen 您能否添加有关如何完成此操作的更多信息?如果我们能让这个很棒的答案变得更好,那就太好了。
@AndHeiberg 检查objc_setAssociatedObject
和objc_removeAssociatedObjects
。
@LeoDabus 你怎么用 dataTaskWithURL 而不是 downloadTaskWithURL?
第一个下载到内存第二个到文件【参考方案2】:
(Swift 4 更新) 为了直接回答最初的问题,这里是发布的 Objective-C sn-p 的 swift 等价物。
let url = URL(string: image.url)
let data = try? Data(contentsOf: url!) //make sure your image in this url does exist, otherwise unwrap in a if let check / try-catch
imageView.image = UIImage(data: data!)
免责声明:
需要注意的是,Data(contentsOf:)
方法会在代码执行的同一线程中同步下载 url 的内容,所以不要调用它在应用程序的主线程中。
使相同代码异步运行而不阻塞 UI 的简单方法是使用 GCD:
let url = URL(string: image.url)
DispatchQueue.global().async
let data = try? Data(contentsOf: url!) //make sure your image in this url does exist, otherwise unwrap in a if let check / try-catch
DispatchQueue.main.async
imageView.image = UIImage(data: data!)
也就是说,在现实生活中的应用程序中,如果您希望获得最佳用户体验并避免多次下载同一张图像,您可能还希望不仅下载它们,而且还希望它们被缓存。已经有相当多的库可以非常无缝地做到这一点,而且它们都非常易于使用。我个人推荐Kingfisher:
import Kingfisher
let url = URL(string: "url_of_your_image")
// this downloads the image asynchronously if it's not cached yet
imageView.kf.setImage(with: url)
就是这样
【讨论】:
"Easily" 是主观的,新手编码人员不会想到这种不受欢迎的行为,但会照原样复制/粘贴它。也许你可以更新你的答案? 我还是不同意。有些答案应该保持简单,很多来这里的人都喜欢复制和粘贴小的sn-ps代码。我刚刚将原始问题中的 Objective-C 代码翻译成 Swift,除此之外的所有内容都是额外的。顺便说一句,提供这种奖励信息的同一个问题已经有一个答案,这对重复信息的意义不大。 @User 只需将image.url
替换为任何字符串 url,无论是否硬编码 :)
@IanWarburton 当然你可以使用任何你想要的东西:)。 1)这个答案基于原始问题,它在objective-C中使用相同的方法,所以我只是帮助“翻译”到swift。 2)删除这种方法并放置URLSession.dataTask
是没有意义的,因为这里的许多其他答案已经显示了如何做到这一点,最好保持不同的选项打开。
嘿嘿,建议的框架,翠鸟,如答案中所述,将完成所有繁重的工作。感谢您的建议!【参考方案3】:
如果您只想加载图像(异步!) - 只需将这个小扩展添加到您的 swift 代码中:
extension UIImageView
public func imageFromUrl(urlString: String)
if let url = NSURL(string: urlString)
let request = NSURLRequest(URL: url)
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue())
(response: NSURLResponse?, data: NSData?, error: NSError?) -> Void in
if let imageData = data as NSData?
self.image = UIImage(data: imageData)
并以这种方式使用它:
myImageView.imageFromUrl("https://robohash.org/123.png")
【讨论】:
它没有出现,我认为是因为需要 GCD。如何刷新? @jo3birdtalk 这不是 GCD 的问题。用视图检查你的绑定。 NSURLConnection 在 iOS 9 等上已弃用。直接使用 NSURLSession。 sendAsynchronousRequest 在 iOS 9 中已弃用 感谢skywinder,我正在使用这种方法从数组中下载图像。我希望当用户按下cancel
按钮时它会停止下载。你知道我怎么能用这种方法做到这一点吗?我需要添加取消功能。【参考方案4】:
Swift 2.2 || Xcode 7.3
我得到了惊人的结果!!使用 AlamofireImage 快速库
它提供了多种功能,例如:
异步下载 如果应用程序出现内存警告,则自动清除图像缓存 图片 URL 缓存 图像缓存 避免重复下载并且非常容易为您的应用实现
Step.1 安装 pod
Alamofire 3.3.x
pod 'Alamofire'
AlamofireImage 2.4.x
pod 'AlamofireImage'
Step.2 导入和使用
import Alamofire
import AlamofireImage
let downloadURL = NSURL(string: "http://cdn.sstatic.net/Sites/***/company/Img/photos/big/6.jpg?v=f4b7c5fee820")!
imageView.af_setImageWithURL(downloadURL)
就是这样!!它会照顾好一切
非常感谢Alamofire guys,让 iDevelopers 的生活变得轻松;)
【讨论】:
Swift 3:foregroundImage.af_setImage(withURL: downloadURL as URL) 谢谢,伙计,这对我有帮助。【参考方案5】:Xcode 12 • Swift 5
Leo Dabus 的回答太棒了!我只是想提供一个多合一的功能解决方案:
if let url = URL(string: "http://www.apple.com/euro/ios/ios8/a/generic/images/og.png")
let task = URLSession.shared.dataTask(with: url) data, response, error in
guard let data = data, error == nil else return
DispatchQueue.main.async /// execute on main thread
self.imageView.image = UIImage(data: data)
task.resume()
【讨论】:
【参考方案6】:斯威夫特 4::
这将在加载图像时显示加载器。 你可以使用NSCache临时存储图片
let imageCache = NSCache<NSString, UIImage>()
extension UIImageView
func loadImageUsingCache(withUrl urlString : String)
let url = URL(string: urlString)
if url == nil return
self.image = nil
// check cached image
if let cachedImage = imageCache.object(forKey: urlString as NSString)
self.image = cachedImage
return
let activityIndicator: UIActivityIndicatorView = UIActivityIndicatorView.init(activityIndicatorStyle: .gray)
addSubview(activityIndicator)
activityIndicator.startAnimating()
activityIndicator.center = self.center
// if not, download image from url
URLSession.shared.dataTask(with: url!, completionHandler: (data, response, error) in
if error != nil
print(error!)
return
DispatchQueue.main.async
if let image = UIImage(data: data!)
imageCache.setObject(image, forKey: urlString as NSString)
self.image = image
activityIndicator.removeFromSuperview()
).resume()
用法:-
truckImageView.loadImageUsingCache(withUrl: currentTruck.logoString)
【讨论】:
它有效。在错误情况下也只需复制 activityIndicator 删除即可。【参考方案7】:我将问题最佳答案的代码封装到一个扩展 UIImageView 的可重用类中,因此您可以直接在情节提要中使用异步加载 UIImageViews(或从代码创建它们)。
这是我的课:
import Foundation
import UIKit
class UIImageViewAsync :UIImageView
override init()
super.init(frame: CGRect())
override init(frame:CGRect)
super.init(frame:frame)
required init(coder aDecoder: NSCoder)
super.init(coder: aDecoder)
func getDataFromUrl(url:String, completion: ((data: NSData?) -> Void))
NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: url)!) (data, response, error) in
completion(data: NSData(data: data))
.resume()
func downloadImage(url:String)
getDataFromUrl(url) data in
dispatch_async(dispatch_get_main_queue())
self.contentMode = UIViewContentMode.ScaleAspectFill
self.image = UIImage(data: data!)
这里是如何使用它:
imageView.downloadImage("http://www.image-server.com/myImage.jpg")
【讨论】:
我们真的需要那些被覆盖的init吗?无论如何,init都不是继承的吗?似乎我们只是在这里覆盖只是为了调用 super 本身,这让我觉得是多余的。【参考方案8】:带有错误处理的swift 3
let url = URL(string: arr[indexPath.row] as! String)
if url != nil
DispatchQueue.global().async
let data = try? Data(contentsOf: url!) //make sure your image in this url does exist, otherwise unwrap in a if let check / try-catch
DispatchQueue.main.async
if data != nil
cell.imgView.image = UIImage(data:data!)
else
cell.imgView.image = UIImage(named: "default.png")
带有扩展名
extension UIImageView
func setCustomImage(_ imgURLString: String?)
guard let imageURLString = imgURLString else
self.image = UIImage(named: "default.png")
return
DispatchQueue.global().async [weak self] in
let data = try? Data(contentsOf: URL(string: imageURLString)!)
DispatchQueue.main.async
self?.image = data != nil ? UIImage(data: data!) : UIImage(named: "default.png")
扩展使用
myImageView. setCustomImage("url")
支持缓存
let imageCache = NSCache<NSString, UIImage>()
extension UIImageView
func loadImageUsingCacheWithURLString(_ URLString: String, placeHolder: UIImage?)
self.image = nil
if let cachedImage = imageCache.object(forKey: NSString(string: URLString))
self.image = cachedImage
return
if let url = URL(string: URLString)
URLSession.shared.dataTask(with: url, completionHandler: (data, response, error) in
//print("RESPONSE FROM API: \(response)")
if error != nil
print("ERROR LOADING IMAGES FROM URL: \(String(describing: error))")
DispatchQueue.main.async [weak self] in
self?.image = placeHolder
return
DispatchQueue.main.async [weak self] in
if let data = data
if let downloadedImage = UIImage(data: data)
imageCache.setObject(downloadedImage, forKey: NSString(string: URLString))
self?.image = downloadedImage
).resume()
【讨论】:
【参考方案9】:let url = NSURL.URLWithString("http://live-wallpaper.net/iphone/img/app/i/p/iphone-4s-wallpapers-mobile-backgrounds-dark_2466f886de3472ef1fa968033f1da3e1_raw_1087fae1932cec8837695934b7eb1250_raw.jpg");
var err: NSError?
var imageData :NSData = NSData.dataWithContentsOfURL(url,options: NSDataReadingOptions.DataReadingMappedIfSafe, error: &err)
var bgImage = UIImage(data:imageData)
【讨论】:
fatal error: unexpectedly found nil while unwrapping an Optional value
你能写出异步的东西吗?
@JedGrant 你可以使用 dispatch_async 进入另一个线程和回调
我必须用“!”解开 NSData (注意最后的 ! )让它像这样工作: var imageData :NSData = NSData(contentsOfURL: url, options: NSDataReadingOptions.DataReadingMappedIfSafe, error: &err)!【参考方案10】:
仅供参考:对于 swift-2.0 Xcode7.0 beta2
extension UIImageView
public func imageFromUrl(urlString: String)
if let url = NSURL(string: urlString)
let request = NSURLRequest(URL: url)
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue())
(response: NSURLResponse?, data: NSData?, error: NSError?) -> Void in
self.image = UIImage(data: data!)
【讨论】:
NSURLConnection 已弃用。你必须使用 NSURLSession。使用 Swift 2.0 和 Xcode 7 编码会更好【参考方案11】:Swift 4: 使用 NSCache 并始终在主线程上运行的小图像(例如:缩略图)的简单加载器:
class ImageLoader
private static let cache = NSCache<NSString, NSData>()
class func image(for url: URL, completionHandler: @escaping(_ image: UIImage?) -> ())
DispatchQueue.global(qos: DispatchQoS.QoSClass.background).async
if let data = self.cache.object(forKey: url.absoluteString as NSString)
DispatchQueue.main.async completionHandler(UIImage(data: data as Data))
return
guard let data = NSData(contentsOf: url) else
DispatchQueue.main.async completionHandler(nil)
return
self.cache.setObject(data, forKey: url.absoluteString as NSString)
DispatchQueue.main.async completionHandler(UIImage(data: data as Data))
用法:
ImageLoader.image(for: imageURL) image in
self.imageView.image = image
【讨论】:
private let 缓存给了我错误,所以将其更改为静态并且它有效!谢谢 我认为你不应该在主线程上加载UIImage(data: data as Data)
。这应该在后台线程中完成。【参考方案12】:
你会想要做的:
UIImage(data: data)
在 Swift 中,他们用常规构造函数替换了大多数 Objective C 工厂方法。
见:
https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithObjective-CAPIs.html#//apple_ref/doc/uid/TP40014216-CH4-XID_26
【讨论】:
技术映射,未替换。如果您创建自己的方法+(instancetype)[MyThing thingWithOtherThing:]
,您也可以在 Swift 中将其称为MyThing(otherThing: ...)
。【参考方案13】:
带有错误句柄和自定义请求标头的 Swift 2
只需给 UIImageView 添加扩展:
extension UIImageView
public func imageFromUrl(urlString: String)
if let url = NSURL(string: urlString)
let request = NSMutableURLRequest(URL: url)
request.setValue("<YOUR_HEADER_VALUE>", forHTTPHeaderField: "<YOUR_HEADER_KEY>")
NSURLSession.sharedSession().dataTaskWithRequest(request)
(data, response, error) in
guard let data = data where error == nil else
NSLog("Image download error: \(error)")
return
if let httpResponse = response as? NSHTTPURLResponse
if httpResponse.statusCode > 400
let errorMsg = NSString(data: data, encoding: NSUTF8StringEncoding)
NSLog("Image download error, statusCode: \(httpResponse.statusCode), error: \(errorMsg!)")
return
dispatch_async(dispatch_get_main_queue(),
NSLog("Image download success")
self.image = UIImage(data: data)
)
.resume()
然后,使用新的imageFromUrl(urlString: String)
下载图片
用法:
imageView.imageFromUrl("https://i.imgur.com/ONaprQV.png")
【讨论】:
在 swift 3 中,我不断收到此错误。有什么问题 ?在这一行中“”“URLSession.shared.dataTask(with: url)”“”“”“”不能调用'dataTask'类型的参数列表'(with: URL, (Data?, URLResponse?, Error? ) -> 无效)'【参考方案14】:斯威夫特 4
此方法将从网站异步下载图片并缓存:
func getImageFromWeb(_ urlString: String, closure: @escaping (UIImage?) -> ())
guard let url = URL(string: urlString) else
return closure(nil)
let task = URLSession(configuration: .default).dataTask(with: url) (data, response, error) in
guard error == nil else
print("error: \(String(describing: error))")
return closure(nil)
guard response != nil else
print("no response")
return closure(nil)
guard data != nil else
print("no data")
return closure(nil)
DispatchQueue.main.async
closure(UIImage(data: data!))
; task.resume()
使用中:
getImageFromWeb("http://www.apple.com/euro/ios/ios8/a/generic/images/og.png") (image) in
if let image = image
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
imageView.image = image
self.view.addSubview(imageView)
// if you use an Else statement, it will be in background
【讨论】:
它是如何缓存的?持续多久? 好像是永久保存的,很奇怪,保存在Library>Caches文件夹中。在模拟器上运行时,使用 print(NSHomeDirectory()) 到达您计算机上的此位置。【参考方案15】:Kingfisher 是将图像加载到 URL 的最佳库之一。
Github 网址 - https://github.com/onevcat/Kingfisher
// If you want to use Activity Indicator.
imageview_pic.kf.indicatorType = .activity
imageview_pic.kf.setImage(with: URL(string: "Give your url string"))
// If you want to use custom placeholder image.
imageview_pic.kf.setImage(with: URL(string: "Give your url string"), placeholder: UIImage(named: "placeholder image name"), options: nil, progressBlock: nil, completionHandler: nil)
【讨论】:
【参考方案16】:斯威夫特 5
extension UIImageView
func load(url: URL)
DispatchQueue.global().async [weak self] in
if let data = try? Data(contentsOf: url)
if let image = UIImage(data: data)
DispatchQueue.main.async
self?.image = image
使用
override func awakeFromNib()
super.awakeFromNib()
if let url = URL(string:"<imageURLHere>")
imgView.load(url: url)
【讨论】:
【参考方案17】:这是从 URL 加载/下载图像的工作代码。 NSCache 自动并在下载和加载实际图像之前显示占位符图像(Swift 4 | Swift 5 代码)。
func NKPlaceholderImage(image:UIImage?, imageView:UIImageView?,imgUrl:String,compate:@escaping (UIImage?) -> Void)
if image != nil && imageView != nil
imageView!.image = image!
var urlcatch = imgUrl.replacingOccurrences(of: "/", with: "#")
let documentpath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
urlcatch = documentpath + "/" + "\(urlcatch)"
let image = UIImage(contentsOfFile:urlcatch)
if image != nil && imageView != nil
imageView!.image = image!
compate(image)
else
if let url = URL(string: imgUrl)
DispatchQueue.global(qos: .background).async
() -> Void in
let imgdata = NSData(contentsOf: url)
DispatchQueue.main.async
() -> Void in
imgdata?.write(toFile: urlcatch, atomically: true)
let image = UIImage(contentsOfFile:urlcatch)
compate(image)
if image != nil
if imageView != nil
imageView!.image = image!
这样使用:
// Here imgPicture = your imageView
// UIImage(named: "placeholder") is Display image brfore download and load actual image.
NKPlaceholderImage(image: UIImage(named: "placeholder"), imageView: imgPicture, imgUrl: "Put Here your server image Url Sting") (image) in
【讨论】:
唯一对我有用的。我的问题是我正在实现音乐播放器,我想在手机关闭时将图像加载到通知中,并且处理程序采用 UIImage 的类型,所以我不得不使用这个方法。 您如何处理下载文件夹的偶尔清理?与 NSCache 相比,图像的重新加载更快、更流畅。但它不希望将图像下载到磁盘。【参考方案18】:Swift 2.0:
1)
if let url = NSURL(string: "http://etc...")
if let data = NSData(contentsOfURL: url)
imageURL.image = UIImage(data: data)
或
imageURL.image =
NSURL(string: "http:// image name...")
.flatMap NSData(contentsOfURL: $0)
.flatMap UIImage(data: $0)
2) 将此方法添加到 VC 或 Extension。
func load_image(urlString:String)
let imgURL: NSURL = NSURL(string: urlString)!
let request: NSURLRequest = NSURLRequest(URL: imgURL)
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()) (response: NSURLResponse?, data: NSData?, error: NSError?) in
if error == nil
self.image_element.image = UIImage(data: data)
用法:
self.load_image(" url strig here")
【讨论】:
sendAsynchronousRequest 在 iOS 9 中已弃用【参考方案19】:class ImageStore: NSObject
static let imageCache = NSCache<NSString, UIImage>()
extension UIImageView
func url(_ url: String?)
DispatchQueue.global().async [weak self] in
guard let stringURL = url, let url = URL(string: stringURL) else
return
func setImage(image:UIImage?)
DispatchQueue.main.async
self?.image = image
let urlToString = url.absoluteString as NSString
if let cachedImage = ImageStore.imageCache.object(forKey: urlToString)
setImage(image: cachedImage)
else if let data = try? Data(contentsOf: url), let image = UIImage(data: data)
DispatchQueue.main.async
ImageStore.imageCache.setObject(image, forKey: urlToString)
setImage(image: image)
else
setImage(image: nil)
用法:
let imageView = UIImageView()
imageView.url("image url")
【讨论】:
【参考方案20】:如果您想快速检查来自 url 的图像,请快速破解
let imageURL = NSURL(string: "https://farm2.staticflickr.com/1591/26078338233_d1466b7da2_m.jpg")
let imagedData = NSData(contentsOfURL: imageURL!)!
imageView?.image = UIImage(data: imagedData)
我在一个表格视图中实现了一个自定义单元格,该单元格只有一个图像
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
let cell = tableView.dequeueReusableCellWithIdentifier("theCell", forIndexPath: indexPath) as! customTableViewCell
let imageURL = NSURL(string: "https://farm2.staticflickr.com/1591/26078338233_d1466b7da2_m.jpg")
let imagedData = NSData(contentsOfURL: imageURL!)!
cell.imageView?.image = UIImage(data: imagedData)
return cell
【讨论】:
你是对的,那是因为下载和压缩是在主线程上进行的。理想情况下,您应该在后台线程中异步执行这两个步骤,可能正在使用 dispatch_async 全局队列 是的,通过使用后台线程我们可以优化下载速度,如果需要我们可以改变逻辑而不是这个,我们可以使用 sdWebImage 或其他框架。【参考方案21】:Swift 4.1 我创建了一个函数,只传递图像 url,生成图像后的缓存键将其设置为完成块。
class NetworkManager: NSObject
private var imageQueue = OperationQueue()
private var imageCache = NSCache<AnyObject, AnyObject>()
func downloadImageWithUrl(imageUrl: String, cacheKey: String, completionBlock: @escaping (_ image: UIImage?)-> Void)
let downloadedImage = imageCache.object(forKey: cacheKey as AnyObject)
if let _ = downloadedImage as? UIImage
completionBlock(downloadedImage as? UIImage)
else
let blockOperation = BlockOperation()
blockOperation.addExecutionBlock(
let url = URL(string: imageUrl)
do
let data = try Data(contentsOf: url!)
let newImage = UIImage(data: data)
if newImage != nil
self.imageCache.setObject(newImage!, forKey: cacheKey as AnyObject)
self.runOnMainThread
completionBlock(newImage)
else
completionBlock(nil)
catch
completionBlock(nil)
)
self.imageQueue.addOperation(blockOperation)
blockOperation.completionBlock =
print("Image downloaded \(cacheKey)")
extension NetworkManager
fileprivate func runOnMainThread(block:@escaping ()->Void)
if Thread.isMainThread
block()
else
let mainQueue = OperationQueue.main
mainQueue.addOperation(
block()
)
【讨论】:
【参考方案22】:获取安全且适用于 Swift 2.0 和 X-Code 7.1 的图像的方法:
static func imageForImageURLString(imageURLString: String, completion: (image: UIImage?, success: Bool) -> Void)
guard let url = NSURL(string: imageURLString),
let data = NSData(contentsOfURL: url),
let image = UIImage(data: data)
else
completion(image: nil, success: false);
return
completion(image: image, success: true)
然后你可以这样调用这个方法:
imageForImageURLString(imageString) (image, success) -> Void in
if success
guard let image = image
else return // Error handling here
// You now have the image.
else
// Error handling here.
如果您使用图像更新视图,则必须在“if success ”之后使用它:
dispatch_async(dispatch_get_main_queue()) () -> Void in
guard let image = image
else return // Error handling here
// You now have the image. Use the image to update the view or anything UI related here
// Reload the view, so the image appears
如果您在 UI 中使用图像,则需要最后一部分的原因是网络调用需要时间。如果您尝试使用图像更新 UI 而不像上面那样调用 dispatch_async ,计算机将在图像仍在获取时查找图像,发现没有图像(还),然后像没有图像一样继续前进成立。将您的代码放入 dispatch_async 完成闭包中,对计算机说:“去,获取此图像,完成后,完成此代码。”这样,当代码被调用时,您将拥有图像,并且一切正常。
【讨论】:
【参考方案23】:我建议使用 Kingfisher 库来异步下载图像。使用 Kingfisher 最好的部分是,它默认缓存所有下载的图像,并将图像 url 作为 id。下次当您请求使用该特定 URl 下载图像时,它将从缓存中加载它。
用法:
newsImage.kf.setImage(with: imageUrl!, placeholder: nil, options: nil, progressBlock: nil, completionHandler: (image, error, cacheType, imageUrl) in
if error == nil
self.activityIndicator.stopAnimating()
else if error != nil
self.activityIndicator.stopAnimating()
)
【讨论】:
【参考方案24】:您可以使用 pod SDWebImage
来实现相同的目的。它易于使用。你可以在这里获取文档SDWebImage
这里是示例代码
self.yourImage.sd_setImage(with: NSURL(string: StrUrl as String ) as URL!, placeholderImage: placeholderImage, options: SDWebImageOptions(rawValue: 0), completed: (image, error, cacheType, imageURL) in
if( error != nil)
print("Error while displaying image" , (error?.localizedDescription)! as String)
)
【讨论】:
【参考方案25】:从服务器加载图像:-
func downloadImage(from url: URL , success:@escaping((_ image:UIImage)->()),failure:@escaping ((_ msg:String)->()))
print("Download Started")
getData(from: url) data, response, error in
guard let data = data, error == nil else
failure("Image cant download from G+ or fb server")
return
print(response?.suggestedFilename ?? url.lastPathComponent)
print("Download Finished")
DispatchQueue.main.async()
if let _img = UIImage(data: data)
success(_img)
func getData(from url: URL, completion: @escaping (Data?, URLResponse?, Error?) -> ())
URLSession.shared.dataTask(with: url, completionHandler: completion).resume()
用法:-
if let url = URL(string: "http://www.apple.com/euro/ios/ios8/a/generic/images/og.png")
self.downloadImage(from:url , success: (image) in
print(image)
, failure: (failureReason) in
print(failureReason)
)
【讨论】:
【参考方案26】:Swift 4.2 和 AlamofireImage
如果使用库不是问题,您可以在AlamofireImage
的帮助下完成。
我的样本来自它的Github
占位符图片示例:
let imageView = UIImageView(frame: frame)
let url = URL(string: "https://httpbin.org/image/png")!
let placeholderImage = UIImage(named: "placeholder")!
imageView.af_setImage(withURL: url, placeholderImage: placeholderImage)
它有许多方便的功能和扩展来处理图像。从缓存到缩放和调整大小,甚至在图像上应用过滤器。如果图像在您的应用中很重要,我建议使用此框架并节省您的时间。
【讨论】:
【参考方案27】:AsyncImage
在iOS 15之后正式引入,一个同步加载和显示图片的视图。
var imageView : AsyncImage
imageView = AsyncImage(url: URL(string: entry.photo))
.frame(width: 200, height: 200)
它还支持:
使用init(url:scale:content:placeholder:) 指定自定义占位符。 获得对加载过程的更多控制,使用init(url:scale:transaction:content:)在doc中查看更多信息
【讨论】:
【参考方案28】:唯一缺少的是一个!
let url = NSURL.URLWithString("http://live-wallpaper.net/iphone/img/app/i/p/iphone-4s-wallpapers-mobile-backgrounds-dark_2466f886de3472ef1fa968033f1da3e1_raw_1087fae1932cec8837695934b7eb1250_raw.jpg");
var err: NSError?
var imageData :NSData = NSData.dataWithContentsOfURL(url!,options: NSDataReadingOptions.DataReadingMappedIfSafe, error: &err)
var bgImage = UIImage(data:imageData!)
【讨论】:
抱歉回复晚了,您能更准确地说明您遇到的错误吗?【参考方案29】:将图像下载到文件的 Swift 2.x 答案(与 Leo Dabus 的答案相反,它将图像存储在内存中)。基于 Leo Dabus 的回答和 Rob 来自Get the data from NSURLSession DownloadTaskWithRequest from completion handler 的回答:
// Set download vars
let downloadURL = NSURL() // URL to download from
let localFilename = "foobar.png" // Filename for storing locally
// Create download request
let task = NSURLSession.sharedSession().downloadTaskWithURL(downloadURL) location, response, error in
guard location != nil && error == nil else
print("Error downloading message: \(error)")
return
// If here, no errors so save message to permanent location
let fileManager = NSFileManager.defaultManager()
do
let documents = try fileManager.URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: false)
let fileURL = documents.URLByAppendingPathComponent(localFilename)
try fileManager.moveItemAtURL(location!, toURL: fileURL)
self.doFileDownloaded(fileURL, localFilename: localFilename)
print("Downloaded message @ \(localFilename)")
catch
print("Error downloading message: \(error)")
// Start download
print("Starting download @ \(downloadURL)")
task.resume()
// Helper function called after file successfully downloaded
private func doFileDownloaded(fileURL: NSURL, localFilename: String)
// Do stuff with downloaded image
【讨论】:
【参考方案30】:已针对 09/2021 的最新更改进行编辑
// It's better to use extension
extension UIImageView
func downloadImage(from URLString: String, with completion: @escaping (_ response: (status: Bool, image: UIImage? ) ) -> Void)
guard let url = URL(string: URLString) else
completion((status: false, image: nil))
return
URLSession.shared.dataTask(with: url) data, response, error in
guard error == nil else
completion((status: false, image: nil))
return
guard let httpURLResponse = response as? HTTPURLResponse,
httpURLResponse.statusCode == 200,
let data = data else
completion((status: false, image: nil))
return
let image = UIImage(data: data)
completion((status: true, image: image))
.resume()
快乐编码。干杯:)
【讨论】:
以上是关于在 Swift 上从 URL 加载/下载图像的主要内容,如果未能解决你的问题,请参考以下文章