显示来自 URL、Swift 4.2 的图像
Display image from URL, Swift 4.2
我是一个相当不错的 Objective C 开发人员,我现在正在学习 Swift(我发现其中相当困难,不仅因为新概念,例如可选值,还因为Swift 不断发展,许多可用的教程已经严重过时了)。
目前我正在尝试将 JSON 从 url 解析为 NSDictionary,然后使用其值之一来显示图像(也是 url)。像这样:
URL -> NSDictionary -> 从 url 初始化 UIImage -> 在 UIImageView 中显示 UIImage
这在 Objective C 中很容易(甚至可能有更短的答案):
NSURL *url = [NSURL URLWithString:@"https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY"];
NSData *apodData = [NSData dataWithContentsOfURL:url];
NSDictionary *apodDict = [NSJSONSerialization JSONObjectWithData:apodData options:0 error:nil];
上面的代码片段给我返回了一个标准的NSDictionary,我可以在其中引用"url"键来获取我要显示的图像的地址:
"url":“https://apod.nasa.gov/apod/image/1811/hillpan_apollo15_4000.jpg”
然后我将其转换为 UIImage 并将其提供给 UIImageView:
NSURL *imageURL = [NSURL URLWithString: [apodDict objectForKey:@"url"]];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
UIImage *apodImage = [UIImage imageWithData:imageData];
UIImageView *apodView = [[UIImageView alloc] initWithImage: apodImage];
现在,我基本上是在尝试在 Swift 中复制上面的 Objective C 代码,但不断地 运行 到墙上。我尝试了几个教程(其中一个实际上做了完全相同的事情:显示 NASA 图像),并找到了一些堆栈溢出答案,但 none 可能会有所帮助,因为它们已经过时或者它们做的事情与我需要的。
所以,我想请社区提供 Swift 4 代码来解决这些问题:
1. Convert data from url into a Dictionary
2. Use key:value pair from dict to get url to display an image
如果还不算太多,我还想在代码旁边索取详细说明,因为我希望答案是针对这项任务的全面 "tutorial",我认为目前还没有随处可用。
谢谢!
首先我很确定半年后你会发现Objective-C非常复杂和困难。
其次,甚至不鼓励使用 ObjC 代码。不要使用同步 Data(contentsOf
方法从远程 URL 加载数据。无论使用哪种语言,都使用异步方式,如 (NS)URLSession
.
并且不要在 Swift 中使用 Foundation 集合类型 NSArray
和 NSDictionary
。如果有本地 Swift 对应项,基本上根本不要使用 NS...
类。
在Swift 4中,你可以很容易地将JSON与Decodable
协议直接解码成一个(Swift)结构,
URL 字符串甚至可以解码为 URL
.
创建结构
struct Item: Decodable {
// let copyright, date, explanation: String
// let hdurl: String
// let mediaType, serviceVersion, title: String
let url: URL
}
如果您需要的不仅仅是 URL,请取消注释这些行。
并用两个数据任务加载数据。
let url = URL(string: "https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY")!
let task = URLSession.shared.dataTask(with: url) { (data, _, error) in
if let error = error { print(error); return }
do {
let decoder = JSONDecoder()
// this line is only needed if all JSON keys are decoded
decoder.keyDecodingStrategy = .convertFromSnakeCase
let result = try decoder.decode(Item.self, from: data!)
let imageTask = URLSession.shared.dataTask(with: result.url) { (imageData, _, imageError) in
if let imageError = imageError { print(imageError); return }
DispatchQueue.main.async {
let apodImage = UIImage(data: imageData!)
let apodView = UIImageView(image: apodImage)
// do something with the image view
}
}
imageTask.resume()
} catch { print(error) }
}
task.resume()
需要将url转换成字符串和数据添加到imageview
let imageURL:URL=URL(string: YourImageURL)!
let data=NSData(contentsOf: imageURL)
Yourimage.image=UIImage(data: data! as Data)
由于图像加载是一项微不足道的任务,同时可以通过多种不同的方式实现,我建议您不要 "reinvent the wheel" 并查看图像加载库,例如 Nuke,因为它已经涵盖了你在开发过程中可能需要的大部分情况。
它允许您使用简单的 api:
异步加载图像并将其显示到您的视图中
Nuke.loadImage(with: url, into: imageView)
如果您需要 - 指定如何加载和显示图像:
let options = ImageLoadingOptions(
placeholder: UIImage(named: "placeholder"),
failureImage: UIImage(named: "failure_image"),
contentModes: .init(
success: .scaleAspectFill,
failure: .center,
placeholder: .center
)
)
Nuke.loadImage(with: url, options: options, into: imageView)
首先在Podfile中添加pod
吊舱 'Alamofire',
吊舱 'AlamofireImage'
你可以检查这个 link 安装 pods => https://cocoapods.org/pods/AlamofireImage
// 使用此函数从 URL 加载图像到 imageview
imageView.af_setImage(
withURL: url,
placeholderImage: placeholderImage //its optional if you want to add placeholder
)
检查此 link 以获取 alamofireImage 的方法
https://github.com/Alamofire/AlamofireImage/blob/master/Documentation/AlamofireImage%203.0%20Migration%20Guide.md
创建 UIIimageView 扩展和以下代码
extension UIImageView {
public func imageFromServerURL(urlString: String) {
self.image = nil
let urlStringNew = urlString.replacingOccurrences(of: " ", with: "%20")
URLSession.shared.dataTask(with: NSURL(string: urlStringNew)! as URL, completionHandler: { (data, response, error) -> Void in
if error != nil {
print(error as Any)
return
}
DispatchQueue.main.async(execute: { () -> Void in
let image = UIImage(data: data!)
self.image = image
})
}).resume()
}}
和
self.UploadedImageView.imageFromServerURL(urlString: imageURLStirng!)
我刚刚扩展了 vadian 的回答,分离了一些问题以清楚地了解基础知识。他的回答应该足够了。
首先,您必须构建自己的结构。这将代表您从网络服务中检索到的 JSON 结构。
struct Item: Codable {
let url, hdurl : URL,
let copyright, explanation, media_type, service_version, title : String
}
然后让你请求方法。我通常为它创建一个单独的文件。现在,vadian 提到了完成处理程序。这些由转义闭包表示。在这里,闭包 ()-> 在两个函数上传递,并以解码数据作为参数调用。
struct RequestCtrl {
func fetchItem(completion: @escaping (Item?)->Void) {
let url = URL(string: "https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY")!
//URLSessionDataTask handles the req and returns the data which you will decode based on the Item structure we defined above.
let task = URLSession.shared.dataTask(with: url) { (data, _, _) in
let jsonDecoder = JSONDecoder()
if let data = data,
let item = try? jsonDecoder.decode(Item.self, from: data){
//jsonDecoder requires a type of our structure represented by .self and the data from the request.
completion(item)
} else {
completion(nil)
}
}
task.resume()
}
func fetchItemPhoto(usingURL url: URL, completion: @escaping (Data?)-> Void) {
let task = URLSession.shared.dataTask(with: url) { (data, _, _) in
if let data = data { completion(data) } else { completion(nil) }
}
task.resume()
}
}
现在在你 ViewController 中,调用你的请求并处理你的闭包的执行。
class ViewController: UIViewController {
let requestCtrl = RequestCtrl()
override func viewDidLoad() {
super.viewDidLoad()
requestCtrl.fetchItem { (fetchedItem) in
guard let fetchedItem = fetchedItem else { return }
self.getPhoto(with: fetchedItem)
}
}
func getPhoto(with item: Item) {
requestCtrl.fetchItemPhoto(usingURL: item.url) { (fetchedPhoto) in
guard let fetchedPhoto = fetchedPhoto else { return }
let photo = UIImage(data: fetchedPhoto)
//now you have a photo at your disposal
}
}
}
这些不是最佳实践,因为我还在学习,所以一定要对主题进行一些研究,尤其是闭包、ios 苹果文档中的并发和 URLComponents :)
您可以使用此扩展程序
extension UIImage {
public static func loadFrom(url: URL, completion: @escaping (_ image: UIImage?) -> ()) {
DispatchQueue.global().async {
if let data = try? Data(contentsOf: url) {
DispatchQueue.main.async {
completion(UIImage(data: data))
}
} else {
DispatchQueue.main.async {
completion(nil)
}
}
}
}
}
正在使用
guard let url = URL(string: "http://myImage.com/image.png") else { return }
UIImage.loadFrom(url: url) { image in
self.photo.image = image
}
更新 Xcode 13.3 , Swift 5
要从 URL 字符串异步加载图像,请使用此扩展:
extension UIImageView {
public func getImageFromURLString(imageURLString: String) {
guard let imageURL = URL(string: imageURLString) else { return}
Task {
await requestImageFromURL(imageURL)
}
}
private func requestImageFromURL(_ imageURL: URL) async{
let urlRequest = URLRequest(url: imageURL)
do {
let (data, response) = try await URLSession.shared.data(for: urlRequest)
if let httpResponse = response as? HTTPURLResponse{
if httpResponse.statusCode == 200{
print("Fetched image successfully")
}
}
// Loading the image here
self.image = UIImage(data: data)
} catch let error {
print(error)
}
}
}
用法:
imageView.getImageFromURLString(imageURLString: "https://apod.nasa.gov/apod/image/1811/hillpan_apollo15_4000.jpg")
我是一个相当不错的 Objective C 开发人员,我现在正在学习 Swift(我发现其中相当困难,不仅因为新概念,例如可选值,还因为Swift 不断发展,许多可用的教程已经严重过时了)。
目前我正在尝试将 JSON 从 url 解析为 NSDictionary,然后使用其值之一来显示图像(也是 url)。像这样:
URL -> NSDictionary -> 从 url 初始化 UIImage -> 在 UIImageView 中显示 UIImage
这在 Objective C 中很容易(甚至可能有更短的答案):
NSURL *url = [NSURL URLWithString:@"https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY"];
NSData *apodData = [NSData dataWithContentsOfURL:url];
NSDictionary *apodDict = [NSJSONSerialization JSONObjectWithData:apodData options:0 error:nil];
上面的代码片段给我返回了一个标准的NSDictionary,我可以在其中引用"url"键来获取我要显示的图像的地址:
"url":“https://apod.nasa.gov/apod/image/1811/hillpan_apollo15_4000.jpg”
然后我将其转换为 UIImage 并将其提供给 UIImageView:
NSURL *imageURL = [NSURL URLWithString: [apodDict objectForKey:@"url"]];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
UIImage *apodImage = [UIImage imageWithData:imageData];
UIImageView *apodView = [[UIImageView alloc] initWithImage: apodImage];
现在,我基本上是在尝试在 Swift 中复制上面的 Objective C 代码,但不断地 运行 到墙上。我尝试了几个教程(其中一个实际上做了完全相同的事情:显示 NASA 图像),并找到了一些堆栈溢出答案,但 none 可能会有所帮助,因为它们已经过时或者它们做的事情与我需要的。
所以,我想请社区提供 Swift 4 代码来解决这些问题:
1. Convert data from url into a Dictionary
2. Use key:value pair from dict to get url to display an image
如果还不算太多,我还想在代码旁边索取详细说明,因为我希望答案是针对这项任务的全面 "tutorial",我认为目前还没有随处可用。
谢谢!
首先我很确定半年后你会发现Objective-C非常复杂和困难。
其次,甚至不鼓励使用 ObjC 代码。不要使用同步 Data(contentsOf
方法从远程 URL 加载数据。无论使用哪种语言,都使用异步方式,如 (NS)URLSession
.
并且不要在 Swift 中使用 Foundation 集合类型 NSArray
和 NSDictionary
。如果有本地 Swift 对应项,基本上根本不要使用 NS...
类。
在Swift 4中,你可以很容易地将JSON与Decodable
协议直接解码成一个(Swift)结构,
URL 字符串甚至可以解码为 URL
.
创建结构
struct Item: Decodable {
// let copyright, date, explanation: String
// let hdurl: String
// let mediaType, serviceVersion, title: String
let url: URL
}
如果您需要的不仅仅是 URL,请取消注释这些行。
并用两个数据任务加载数据。
let url = URL(string: "https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY")!
let task = URLSession.shared.dataTask(with: url) { (data, _, error) in
if let error = error { print(error); return }
do {
let decoder = JSONDecoder()
// this line is only needed if all JSON keys are decoded
decoder.keyDecodingStrategy = .convertFromSnakeCase
let result = try decoder.decode(Item.self, from: data!)
let imageTask = URLSession.shared.dataTask(with: result.url) { (imageData, _, imageError) in
if let imageError = imageError { print(imageError); return }
DispatchQueue.main.async {
let apodImage = UIImage(data: imageData!)
let apodView = UIImageView(image: apodImage)
// do something with the image view
}
}
imageTask.resume()
} catch { print(error) }
}
task.resume()
需要将url转换成字符串和数据添加到imageview
let imageURL:URL=URL(string: YourImageURL)!
let data=NSData(contentsOf: imageURL)
Yourimage.image=UIImage(data: data! as Data)
由于图像加载是一项微不足道的任务,同时可以通过多种不同的方式实现,我建议您不要 "reinvent the wheel" 并查看图像加载库,例如 Nuke,因为它已经涵盖了你在开发过程中可能需要的大部分情况。
它允许您使用简单的 api:
异步加载图像并将其显示到您的视图中Nuke.loadImage(with: url, into: imageView)
如果您需要 - 指定如何加载和显示图像:
let options = ImageLoadingOptions(
placeholder: UIImage(named: "placeholder"),
failureImage: UIImage(named: "failure_image"),
contentModes: .init(
success: .scaleAspectFill,
failure: .center,
placeholder: .center
)
)
Nuke.loadImage(with: url, options: options, into: imageView)
首先在Podfile中添加pod 吊舱 'Alamofire', 吊舱 'AlamofireImage' 你可以检查这个 link 安装 pods => https://cocoapods.org/pods/AlamofireImage
// 使用此函数从 URL 加载图像到 imageview
imageView.af_setImage(
withURL: url,
placeholderImage: placeholderImage //its optional if you want to add placeholder
)
检查此 link 以获取 alamofireImage 的方法 https://github.com/Alamofire/AlamofireImage/blob/master/Documentation/AlamofireImage%203.0%20Migration%20Guide.md
创建 UIIimageView 扩展和以下代码
extension UIImageView {
public func imageFromServerURL(urlString: String) {
self.image = nil
let urlStringNew = urlString.replacingOccurrences(of: " ", with: "%20")
URLSession.shared.dataTask(with: NSURL(string: urlStringNew)! as URL, completionHandler: { (data, response, error) -> Void in
if error != nil {
print(error as Any)
return
}
DispatchQueue.main.async(execute: { () -> Void in
let image = UIImage(data: data!)
self.image = image
})
}).resume()
}}
和
self.UploadedImageView.imageFromServerURL(urlString: imageURLStirng!)
我刚刚扩展了 vadian 的回答,分离了一些问题以清楚地了解基础知识。他的回答应该足够了。
首先,您必须构建自己的结构。这将代表您从网络服务中检索到的 JSON 结构。
struct Item: Codable {
let url, hdurl : URL,
let copyright, explanation, media_type, service_version, title : String
}
然后让你请求方法。我通常为它创建一个单独的文件。现在,vadian 提到了完成处理程序。这些由转义闭包表示。在这里,闭包 ()-> 在两个函数上传递,并以解码数据作为参数调用。
struct RequestCtrl {
func fetchItem(completion: @escaping (Item?)->Void) {
let url = URL(string: "https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY")!
//URLSessionDataTask handles the req and returns the data which you will decode based on the Item structure we defined above.
let task = URLSession.shared.dataTask(with: url) { (data, _, _) in
let jsonDecoder = JSONDecoder()
if let data = data,
let item = try? jsonDecoder.decode(Item.self, from: data){
//jsonDecoder requires a type of our structure represented by .self and the data from the request.
completion(item)
} else {
completion(nil)
}
}
task.resume()
}
func fetchItemPhoto(usingURL url: URL, completion: @escaping (Data?)-> Void) {
let task = URLSession.shared.dataTask(with: url) { (data, _, _) in
if let data = data { completion(data) } else { completion(nil) }
}
task.resume()
}
}
现在在你 ViewController 中,调用你的请求并处理你的闭包的执行。
class ViewController: UIViewController {
let requestCtrl = RequestCtrl()
override func viewDidLoad() {
super.viewDidLoad()
requestCtrl.fetchItem { (fetchedItem) in
guard let fetchedItem = fetchedItem else { return }
self.getPhoto(with: fetchedItem)
}
}
func getPhoto(with item: Item) {
requestCtrl.fetchItemPhoto(usingURL: item.url) { (fetchedPhoto) in
guard let fetchedPhoto = fetchedPhoto else { return }
let photo = UIImage(data: fetchedPhoto)
//now you have a photo at your disposal
}
}
}
这些不是最佳实践,因为我还在学习,所以一定要对主题进行一些研究,尤其是闭包、ios 苹果文档中的并发和 URLComponents :)
您可以使用此扩展程序
extension UIImage {
public static func loadFrom(url: URL, completion: @escaping (_ image: UIImage?) -> ()) {
DispatchQueue.global().async {
if let data = try? Data(contentsOf: url) {
DispatchQueue.main.async {
completion(UIImage(data: data))
}
} else {
DispatchQueue.main.async {
completion(nil)
}
}
}
}
}
正在使用
guard let url = URL(string: "http://myImage.com/image.png") else { return }
UIImage.loadFrom(url: url) { image in
self.photo.image = image
}
更新 Xcode 13.3 , Swift 5
要从 URL 字符串异步加载图像,请使用此扩展:
extension UIImageView {
public func getImageFromURLString(imageURLString: String) {
guard let imageURL = URL(string: imageURLString) else { return}
Task {
await requestImageFromURL(imageURL)
}
}
private func requestImageFromURL(_ imageURL: URL) async{
let urlRequest = URLRequest(url: imageURL)
do {
let (data, response) = try await URLSession.shared.data(for: urlRequest)
if let httpResponse = response as? HTTPURLResponse{
if httpResponse.statusCode == 200{
print("Fetched image successfully")
}
}
// Loading the image here
self.image = UIImage(data: data)
} catch let error {
print(error)
}
}
}
用法:
imageView.getImageFromURLString(imageURLString: "https://apod.nasa.gov/apod/image/1811/hillpan_apollo15_4000.jpg")