在 Swift 上从 URL 加载/下载图像

Loading/Downloading image from URL on Swift

提问人:QuentR 提问时间:6/16/2014 最后编辑:QuentR 更新时间:11/8/2023 访问量:739638

问:

我想从我的应用程序中的 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.urltest.fr/assets/img/iOS%20icon's%20Style/ios8.png";
 }

有人可以解释一下为什么它不适用于 Swift,以及我该如何解决这个问题。imageWithData:

iOS Swift UI图像 nsurl

评论

4赞 aleclarson 6/16/2014
试试这个imageURL.image = UIImage(data: myDataVar)
0赞 QuentR 6/16/2014
完美,它奏效了!谢谢 但是,我不明白为什么这种方法在目标 C 中有效,而不是在 Swift 中......奇怪
0赞 aleclarson 6/16/2014
当你在使用 Cocoa 类时遇到问题,请尝试 CMD+单击类名,您应该能够看到该类的 Swift 界面!
8赞 Hardik Bar 10/15/2015
if let url = NSURL(string: “imageurl”) { if let data = NSData(contentsOfURL: url) { imageView.image = UIImage(data: data) } }
0赞 Mick MacCallum 4/3/2016
@LeoDabus没关系。据我了解,“swift”标签将始终指当前版本。指定版本的标记用于与版本特定语言功能相关的问题。这不是确定的,但请查看这篇元帖子

答:

8赞 joshstaiger 6/16/2014 #1

您需要执行以下操作:

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

评论

2赞 Brian Nickel 6/17/2014
技术上映射,而不是替换。如果你创建自己的方法,你也会像在 Swift 中一样调用它。+(instancetype)[MyThing thingWithOtherThing:]MyThing(otherThing: ...)
13赞 user3763002 6/21/2014 #2
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)

评论

5赞 User 9/13/2014
fatal error: unexpectedly found nil while unwrapping an Optional value
0赞 Jed Grant 10/16/2014
你有机会写一些异步的东西吗?
0赞 Matej 10/30/2014
@JedGrant您可以使用dispatch_async进入另一个线程和回调
0赞 botbot 11/30/2014
我不得不用“!(注意末尾的 !)让它像这样工作:var imageData :NSData = NSData(contentsOfURL: url, options: NSDataReadingOptions.DataReadingMappedIfSafe, error: &err)!
1赞 Simon Jensen 11/3/2014 #3

唯一缺少的是 !

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!)

评论

0赞 Simon Jensen 5/24/2017
很抱歉回复晚了,你能更准确地说明你得到的错误吗?
433赞 Lucas Eduardo 12/17/2014 #4

(Swift 4 更新)为了直接回答原始问题,这里是已发布的 Objective-C 代码段的 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!)

免責聲明:

需要注意的是,该方法将在执行代码的同一线程中同步下载 url 的内容,因此不要在应用程序的主线程中调用它。Data(contentsOf:)

要使相同的代码异步运行,而不阻塞界面,一种简单的方法是使用 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!)
    }
}

也就是说,在现实生活中的应用程序中,如果您想获得最佳的用户体验并避免多次下载同一图像,您可能还希望不仅下载它们,而且缓存它们。已经有相当多的库可以非常无缝地做到这一点,而且它们都非常易于使用。我个人推荐翠鸟

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) 

也适用于 SwiftUI:

var body: some View {
    KFImage(URL(string: "https://example.com/image.png")!)
}

就是这样

评论

15赞 Orlin Georgiev 4/16/2015
“轻松”是主观的,新手编码人员不会预料到这种不需要的行为,而是按原样复制/粘贴这个被剪掉的。也许你可以更新你的答案?
16赞 Lucas Eduardo 4/17/2015
我仍然不同意。有些答案应该保持简单,很多来到这里的人都喜欢复制和粘贴一小段代码。我刚刚将原始问题的 objective-C 代码翻译成 Swift,除此之外的一切都是奖励。顺便说一句,同样的问题已经有一个答案,提供了这种奖励信息,复制信息没有多大意义。
2赞 Lucas Eduardo 3/29/2016
@User只需将 替换为任何字符串 url,无论是否硬编码 :)image.url
2赞 Lucas Eduardo 8/5/2016
@IanWarburton 当然,你可以:)使用任何你想使用的东西。1)这个答案是基于原始问题,该问题在objective-C中使用了相同的方法,所以我只是帮助“翻译”到swift。2)删除这种方法并放置是没有意义的,因为这里的许多其他答案已经显示了如何做到这一点,最好保持不同的选项打开。URLSession.dataTask
4赞 Kilmazing 3/16/2019
嘿嘿,建议的框架,翠鸟,如答案中所述,将完成所有繁重的工作。谢谢你的建议!
907赞 Leo Dabus 12/31/2014 #5

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")

评论

23赞 Paul Peelen 8/3/2015
这里只是一个旁注,你应该为它设置一个关联的对象;否则,您可能会将图像叠加在一起。例如,在单元格显示图像的情况下,当返回出列的单元格时,图像会更新。如果进程 #1 花费的时间比进程 #2 长,则进程 #2 将显示其映像,然后由进程 #1 更新,即使该映像对用户不再有效。UITableView
4赞 AndHeiberg 2/8/2016
@PaulPeelen您能否添加有关如何执行此操作的更多信息?如果我们能让这个令人敬畏的答案变得更好,那就太好了。
2赞 Paul Peelen 2/8/2016
@AndHeiberg 检查和 .objc_setAssociatedObjectobjc_removeAssociatedObjects
2赞 Crashalot 4/21/2016
@LeoDabus为什么使用 dataTaskWithURL 而不是 downloadTaskWithURL?
5赞 Leo Dabus 4/21/2016
第一个下载到内存,第二个下载到文件
16赞 dy_ 3/9/2015 #6

我将问题的最佳答案的代码包装到一个扩展 UIImageView 的可重用类中,因此您可以直接在情节提要中使用异步加载 UIImageView(或从代码创建它们)。

这是我的班级:

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")

评论

2赞 NYC Tech Engineer 4/19/2016
我们真的需要那些被覆盖的初始化吗?反正 inits 不是继承的吗?似乎我们只是在这里覆盖只是为了称呼超级本身,这让我觉得是多余的。
69赞 skywinder 6/2/2015 #7

如果你只想加载图像(异步!) - 只需将这个小扩展添加到你的 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")

评论

1赞 jo3birdtalk 2/7/2016
它没有出现,我认为是因为需要 GCD。如何刷新?
0赞 skywinder 2/8/2016
@jo3birdtalk这不是 GCD 的问题。使用 view 检查绑定。
9赞 muhasturk 3/11/2016
NSURLConnection 在 iOS 9 上已弃用,依此类推。使用 NSURLSession intead。
2赞 Crashalot 4/21/2016
sendAsynchronousRequest 在 iOS 9 中已弃用
0赞 ZAFAR007 7/19/2016
谢谢 skywinder,我正在使用这种方法从数组下载图像。我希望当用户按下按钮时,它会停止下载。你知道我如何使用这种方法做到这一点吗?我需要添加取消功能。cancel
13赞 katopz 7/13/2015 #8

仅供参考:适用于 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!)
            }
        }
    }
}

评论

5赞 BilalReffas 9/4/2015
NSURLConnection 已弃用。您必须使用 NSURLSession。使用 Swift 2.0 和 Xcode 7 进行编码会更好
3赞 Ben Patch 12/9/2015 #9

一种获取安全且适用于 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完成闭包中,对计算机说,“去,获取这个图像,当你完成后,再完成这个代码。这样,当调用代码时,您将拥有图像,并且一切正常。

4赞 Alvin George 2/17/2016 #10

斯威夫特 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")

评论

0赞 Crashalot 4/21/2016
sendAsynchronousRequest 在 iOS 9 中已弃用
46赞 swiftBoy 3/29/2016 #11

斯威夫特 2.2 ||Xcode 7.3

我得到了惊人的结果!使用 AlamofireImage swift 库

它提供了多种功能,例如:

  • 异步下载
  • 如果应用程序发生内存警告,则自动清除图像缓存
  • 图像 URL 缓存
  • 图像缓存
  • 避免重复下载

并且非常容易为您的应用程序实现

Step.1 安装 Pod


阿拉莫火 3.3.x

豆荚“阿拉莫火”

阿拉莫菲尔图片 2.4.x

pod 'AlamofireImage'

Step.2 导入和使用

import Alamofire
import AlamofireImage

let downloadURL = NSURL(string: "http://cdn.sstatic.net/Sites/stackoverflow/company/Img/photos/big/6.jpg?v=f4b7c5fee820")!
imageView.af_setImageWithURL(downloadURL)

就是这样!!它会照顾好一切


非常感谢 Alamofire 的家伙,让 iDevelopers 的生活变得轻松;)

评论

9赞 thejuki 10/12/2016
Swift 3:foregroundImage.af_setImage(withURL:downloadURL 作为 URL)
-2赞 Bibin Joseph 4/19/2016 #12

在 Swift 中使用此代码

imageView.image=UIImage(data: NSData(contentsOfURL: NSURL(string: "http://myURL/ios8.png")!)!
2赞 Crashalot 4/22/2016 #13

Swift 2.x 应答将图像下载到文件(与 Leo Dabus 的应答相反,后者将图像存储在内存中)。根据 Leo Dabus 的回答和 Rob 的回答,从 NSURLSession 获取数据 DownloadTaskWithRequest from completion 处理程序

    // 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

}
5赞 Naishta 4/28/2016 #14

如果您想快速检查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
        
    }

评论

1赞 Naishta 10/14/2016
你是对的,那是因为下载和压缩发生在主线程上。理想情况下,您应该在后台线程中异步执行这两个步骤,可能正在使用全局队列dispatch_async
0赞 Gourav Joshi 10/14/2016
是的,通过使用后台线程,我们可以优化下载速度,如果需要,我们可以更改逻辑,而不是这样,我们可以使用 sdWebImage 或其他框架。
7赞 Cody 6/22/2016 #15

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")

评论

0赞 Aymen BRomdhane 9/20/2016
在 swift 3 中,我一直收到这个错误。怎么了?在此行中 “”“ URLSession.shared.dataTask(with: url){""""""" 无法使用类型为 '(with: URL, (Data?, URLResponse?, Error?)->虚空)'
53赞 Mark Moeykens 9/24/2016 #16

Xcode 12斯威夫特 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()
}
17赞 Manee ios 3/29/2017 #17

具有错误处理功能的 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()
        }
    }
}
7赞 Bobby 4/30/2017 #18

斯威夫特 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
    }

评论

0赞 Ramis 11/27/2017
缓存是怎么回事?持续多长时间?
0赞 Bobby 11/27/2017
它似乎是永久存储的,这很奇怪,它存储在 Library>Caches 文件夹中。在模拟器上运行时,使用 print(NSHomeDirectory()) 访问计算机上的此位置。
-1赞 FARAZ 6/15/2017 #19

对于 Swift-3 及更高版本:

extension UIImageView {
  public func imageFromUrl(urlString: String) {
    if let url = URL(string: urlString) {
        let request = URLRequest(url: url)
        NSURLConnection.sendAsynchronousRequest(request as URLRequest, queue: .main, completionHandler: { (response, data, error) in
            if let imageData = data as NSData? {
                self.image = UIImage(data: imageData as Data)
            }
        })
    }
  }
}
12赞 fethica 2/17/2018 #20

斯威夫特 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
}

评论

1赞 Hammad Tariq 5/14/2018
private let cache 给我错误,所以把它改成静态,它起作用了!谢谢
0赞 Diz 9/9/2021
我认为您不应该在主线程上加载。这应该在后台线程中完成。UIImage(data: data as Data)
6赞 Kamani Jasmin 4/14/2018 #21

Kingfisher 是将图像加载到 URL 中的最佳库之一。

Github URL - 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)
0赞 saurabh rathod 7/5/2018 #22

使用 Ascyimageview,您可以轻松地在 imageview 中加载 imageurl。

let image1Url:URL = URL(string: “(imageurl)” as String)! imageview.imageURL = image1Url

5赞 Nikunj Kumbhani 8/8/2018 #23

这是从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 }

评论

1赞 JhonnyTawk 10/1/2019
唯一对我有用的人。我的问题是我正在实现音乐播放器,我希望在手机关闭时将图像加载到通知中,并且处理程序采用 UIImage 的类型,所以我不得不使用这种方法。
0赞 kirqe 10/28/2020
如何处理偶尔清理下载文件夹?与 NSCache 相比,图像的重新加载更快、更流畅。但它宁愿不将映像下载到磁盘。
4赞 Gurjinder Singh 8/19/2018 #24

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()
      })
    }
  }
}
3赞 iamVishal16 9/11/2018 #25

已编辑 最新更改 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()
}
}

快乐的溺爱。干杯:)

20赞 Jack 9/14/2018 #26

斯威夫特 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)

评论

2赞 djdance 4/21/2019
它有效。只需在错误情况下复制 activityIndicator 删除。
3赞 Raj Salla 9/27/2018 #27

我建议使用 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()
                }
            })
3赞 Shruti Thombre 10/10/2018 #28

您可以使用 pod 来实现相同的目的。它易于使用。哟可以在这里获得文档 SDWebImageSDWebImage

下面是示例代码

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)
                }
            })
3赞 Abhimanyu Daspan 1/3/2019 #29

从服务器加载图像:-

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)
                        })
                    }
3赞 Hamish 1/10/2019 #30

Swift 4.2 和 AlamofireImage

如果使用库不是问题,您可以借助 . 我的样本来自它的 GithubAlamofireImage

占位符图像示例:

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)

它具有许多方便的功能和扩展来处理图像。从缓存到缩放和调整大小,甚至在图像上应用滤镜。如果图像在您的应用程序中很重要,我建议使用此框架并节省您的时间。

11赞 Amro Jaber 9/24/2019 #31

斯威夫特 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)
   }
}
1赞 Jawad Ali 12/29/2019 #32

为了在轻量级库中使用更好的性能,可以使用轻量级库 智能延迟加载 如果要从 url 异步加载图像,可以使用这种延迟加载方法UITableViewUICollectionView

在 iOS 中使用和 中的智能“延迟加载” 因此,在这个项目中,我们可以通过使用 和 并发来优化应用程序的性能,从而在任何视图 ( 或 ) 中下载多个图像。以下是本项目的关键点 智能延迟加载:创建镜像下载服务。根据单元格的可见性确定下载的优先级。UICollectionViewUITableViewNSOperationNSOperationQueueUICollectionViewUITableViewOperationOperationQueue

ImageDownloadService 类将创建一个单例实例,并具有 NSCache 实例来缓存已下载的图像。我们继承了 Operation 类到 TOperation,以便根据需要修改功能。我认为操作子类的属性在功能方面非常清楚。我们正在使用 KVO 监视操作状态的变化。

1赞 Oded Breiner 4/23/2020 #33

使用 SwiftUI 时,SDWebImageSwiftUI 是最佳选择。

通过 XCode 的 Swift 包管理器添加依赖项:https://github.com/SDWebImage/SDWebImageSwiftUI.git

然后直接使用而不是WebImage()Image()

WebImage(url: URL(string: "https://nokiatech.github.io/heif/content/images/ski_jump_1440x960.heic"))
4赞 Anand Khanpara #34
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")
4赞 RY_ Zheng 6/10/2021 #35

AsyncImage 在 iOS 15 之后正式推出,这是一个同步加载和显示图像的视图。

 var imageView : AsyncImage

imageView = AsyncImage(url: URL(string: entry.photo))
    .frame(width: 200, height: 200)

它还支持:

文档中查看更多内容

1赞 Imran0001 10/13/2021 #36

您可以使用 Kingfisher 轻松地从图像 url 下载图像。

首先将翠鸟作为进口-

pod 'Kingfisher'

然后将其导入到您的类中作为 -

import Kingfisher

之后,添加一个临时的 UIImageView

let imgView = UIImageView()
imgView.kf.setImage(with: yourImageURL)

if let finalImage = imgView.image {
    // finalImage is your image
}
1赞 Casse Nhari 12/16/2021 #37

这是最简单的方法,您不必担心异步或它是如何工作的。

import SDWebImage

imageView.sd_setImage(with: URL(string: ".net/path/to/image.jpg"), placeholderImage: UIImage(named: "placeholder.png"))
Using UIImageView+WebCache category with UI

这是一篇关于它的详细博客文章

评论

0赞 Jeremy Caney 12/16/2021
在引用您自己的内容时,请务必阅读 Stack Overflow 的自我推销政策
0赞 Mahdi Moqadasi 11/17/2022 #38

一个干净的方法:

extension URL {
    var favIcon16: UIImage? { getFav(ofSize: .s) }
    var favIcon32: UIImage? { getFav(ofSize: .m) }
    var favIcon64: UIImage? { getFav(ofSize: .l) }
    var favIcon128: UIImage? { getFav(ofSize: .xl) }
    var favIcon256: UIImage? { getFav(ofSize: .xxl) }
    var favIcon512: UIImage? { getFav(ofSize: .xxxl) }

    private func getFav(ofSize s: FavSize) -> UIImage? {
        guard UIApplication.shared.canOpenURL(self),
              let favUrl = URL(string: "https://www.google.com/s2/favicons?sz=\(s.rawValue)&domain=\(self.absoluteURL)"),
              let data = try? Data(contentsOf: favUrl)
        else { return nil }
        return UIImage(data: data)
 }
    private enum FavSize: Int, CaseIterable { case s = 16, m = 32, l = 64, xl = 128, xxl = 256, xxxl = 512 }
}

和用法:

let myUrl = URL(string: "http://facebook.com")
myImgView.image = myUrl.favIcon16
0赞 Mojtaba Hosseini 11/7/2023 #39

从 Swift 5.5 开始,它就像调用一个函数一样简单:

imageView.setImage(url: myImageURL, placeholder: thePlaceholder)

在这个简单的扩展中使用原生 Swift 并发

extension UIImageView {
    func setImage(url: URL, placeholder: UIImage?) {
        self.image = placeholder

        Task { [weak self] in
            let (data, _) = try await URLSession.shared.data(from: url)
            self?.image = UIImage(data: data)
        }
    }
}

请注意,将使用 直到图像从网络到达。placeholder

此外,我们将 包装在函数中,以便可以同步调用函数本身。Task