Carregando / baixando imagem do URL no Swift

415

Gostaria de carregar uma imagem de uma URL no meu aplicativo, então tentei primeiro com o Objective-C e funcionou, no entanto, com Swift, tenho um erro de compilação:

'imageWithData' está indisponível: use a construção do objeto 'UIImage (dados :)'

Minha função:

@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
}

No Objetivo-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";
 }

Alguém pode me explicar por que imageWithData:isso não funciona com o Swift e como posso resolver o problema.

QuentR
fonte
3
Tente issoimageURL.image = UIImage(data: myDataVar)
aleclarson
Perfeito, funcionou! Obrigado No entanto, não sei por que esse método funciona no Objetivo C, e não no Swift ... Strange
QuentR
Quando você tiver problemas com uma classe Cocoa, tente CMD + clicando no nome da classe e poderá ver a interface Swift da classe!
aleclarson
3
if let url = NSURL (string: "imageurl") {if data data = NSData (contentsOfURL: url) {imageView.image = UIImage (dados: dados)}}
Hardik Bar 15/10
2
@Leo Dabus Esta questão não é específica do Swift 2. Por favor, pare de adicionar essa tag a ela.
Mick MacCallum

Respostas:

770

Xcode 8 ou posterior • Swift 3 ou posterior

Sincronicamente:

if let filePath = Bundle.main.path(forResource: "imageName", ofType: "jpg"), let image = UIImage(contentsOfFile: filePath) {
    imageView.contentMode = .scaleAspectFit
    imageView.image = image
}

Assíncrona:

Crie um método com um manipulador de conclusão para obter os dados da imagem do seu URL

func getData(from url: URL, completion: @escaping (Data?, URLResponse?, Error?) -> ()) {
    URLSession.shared.dataTask(with: url, completionHandler: completion).resume()
}

Crie um método para baixar a imagem (inicie a tarefa)

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")
        DispatchQueue.main.async() { [weak self] in
            self?.imageView.image = UIImage(data: data)
        }
    }
}

Uso:

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    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.")
}

Extensão :

extension UIImageView {
    func downloaded(from url: URL, contentMode mode: UIViewContentMode = .scaleAspectFit) {  // for swift 4.2 syntax just use ===> mode: UIView.ContentMode
        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: UIViewContentMode = .scaleAspectFit) {  // for swift 4.2 syntax just use ===> mode: UIView.ContentMode
        guard let url = URL(string: link) else { return }
        downloaded(from: url, contentMode: mode)
    }
}

Uso:

imageView.downloaded(from: "https://cdn.arstechnica.net/wp-content/uploads/2018/06/macOS-Mojave-Dynamic-Wallpaper-transition.jpg")
Leo Dabus
fonte
13
Apenas uma nota lateral aqui, você deve definir um objeto associado a ele; Caso contrário, você poderá carregar imagens umas sobre as outras. Por exemplo, UITableViewonde uma célula mostra uma imagem e a imagem é atualizada quando a célula desenfileirada é retornada. Se o processo 1 demorar mais do que o processo 2, o processo 2 mostrará sua imagem e, posteriormente, será atualizado pelo processo 1, mesmo que essa imagem não seja mais válida para o usuário.
Paul Peelen
1
Isso me resolveu o meu problema. Quando tento carregar a imagem com o URL na exibição de tabela, a exibição de rolagem não está realmente respondendo ao rolar para cima ou para baixo. Eu usei o UIImageView Extension e resolveu o meu problema. Obrigado.
JengGe Chao 7/03/16
1
@LeoDabus ok thanks. e obrigado por responder a tantas perguntas no SO! você é como um professor de rápida e iOS. :)
Crashalot
2
@LeoDabus, por que você usou o dataTaskWithURL em vez do downloadTaskWithURL?
Crashalot #
3
Os primeiros um download para a memória o segundo para um arquivo
Leo Dabus
350

(Atualização do Swift 4) Para responder diretamente à pergunta original, aqui está o equivalente rápido do snippet Objective-C publicado.

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

AVISO LEGAL:

É importante observar que o Data(contentsOf:)método baixará o conteúdo da URL de forma síncrona no mesmo encadeamento em que o código está sendo executado, portanto , não o invoque no encadeamento principal do seu aplicativo.

Uma maneira fácil de executar o mesmo código de forma assíncrona, sem bloquear a interface do usuário, é usando o 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!)
    }
}

Dito isto, em aplicativos da vida real, se você deseja ter a melhor experiência do usuário e evitar vários downloads da mesma imagem, convém que eles não sejam apenas baixados, mas armazenados em cache. Já existem algumas bibliotecas que fazem isso de maneira transparente e todas elas são realmente fáceis de usar. Eu, pessoalmente, recomendo o 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) 

E é isso

Lucas Eduardo
fonte
12
"Facilmente" é subjetivo, e os codificadores iniciantes não esperam esse comportamento indesejado, mas copiam / colam esse recorte como está. Talvez você possa atualizar sua resposta?
Orlin Georgiev
13
Eu ainda não concordo. Algumas respostas devem ser mantidas simples, muitas pessoas que vêm aqui gostam de copiar e colar pequenos trechos de código. Acabei de traduzir para Swift o código objetivo-C da pergunta original, tudo além disso é um bônus. A propósito, já existe uma resposta nessa mesma pergunta, fornecendo esse tipo de informação de bônus, o que faz menos sentido duplicar informações.
Lucas Eduardo
1
@User basta substituir o image.urlpor qualquer url corda, codificado ou não :)
Lucas Eduardo
1
@IanWarburton Claro que você pode usar o que quiser :). 1) Esta resposta é baseada na pergunta original, que estava usando a mesma abordagem no objetivo-C, então eu apenas ajudei na "tradução" a agilizar. 2) Não faz sentido remover essa abordagem e colocar a questão URLSession.dataTaskporque muitas outras respostas aqui já mostram como fazê-lo. É melhor manter as diferentes opções abertas.
Lucas Eduardo
2
Heyo, a estrutura sugerida, kingfisher, como indicado na resposta, fará todo o trabalho pesado. Obrigado pela sugestão!
Kilmazing
68

Se você deseja apenas carregar a imagem (de forma assíncrona!) - basta adicionar esta pequena extensão ao seu código rápido:

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

E use-o desta maneira:

myImageView.imageFromUrl("https://robohash.org/123.png")
skywinder
fonte
1
Não está aparecendo, e acho que porque há uma necessidade de GCD. Como atualizo?
Jo3birdtalk
@ jo3birdtalk não é um problema do GCD. Verifique suas ligações com vista.
skywinder 8/16
6
O NSURLConnection foi descontinuado no iOS 9 e assim por diante. Use NSURLSession intead.
21416 muhasturk
2
sendAsynchronousRequest é obsoleto em iOS 9
Crashalot
Obrigado skywinder, estou usando este método para baixar imagens da matriz. Quero quando o usuário pressionar o cancelbotão para de baixar. Você tem alguma idéia de como posso fazer isso usando esse método? Preciso adicionar a funcionalidade de cancelamento.
ZAFAR007
44

Swift 2.2 || Xcode 7.3

Eu obtive resultados surpreendentes !! com a biblioteca rápida AlamofireImage

Ele fornece vários recursos, como:

  • Download assíncrono
  • Limpar automaticamente o cache de imagens se ocorrerem avisos de memória para o aplicativo
  • Cache de URL da imagem
  • Cache de imagem
  • Evite downloads duplicados

e muito fácil de implementar para o seu aplicativo

Etapa.1 Instalar pods


Alamofire 3.3.x

pod 'Alamofire'

AlamofireImage 2.4.x

pod 'AlamofireImage'

Etapa.2 importar e usar

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)

é isso aí!! vai cuidar de tudo


Muito obrigado à equipe da Alamofire , por facilitar a vida dos iDevelopers;)

swiftBoy
fonte
8
Swift 3: foregroundImage.af_setImage (withURL: downloadURL como URL)
thejuki
28

Xcode 8Swift 3

A resposta de Leo Dabus é incrível! Eu só queria fornecer uma solução de função multifuncional:

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()
Mark Moeykens
fonte
3
Mark, provavelmente significava "assíncrono" no final lá
Fattie
16

Agrupei o código das melhores respostas para a pergunta em uma única classe reutilizável, estendendo o UIImageView, para que você possa usar diretamente o carregamento assíncrono do UIImageViews no storyboard (ou criá-los a partir do código).

Aqui está a minha turma:

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

e aqui está como usá-lo:

imageView.downloadImage("http://www.image-server.com/myImage.jpg")
datayeah
fonte
2
Nós realmente precisamos daquelas unidades substituídas? As inits não são herdadas, afinal? Parece que estamos substituindo aqui apenas para chamar de super em si, o que me parece redundante.
NYC tecnologia Engineer
16

Swift 4 :

Isso mostra o carregador enquanto carrega a imagem. Você pode usar o NSCache, que armazena imagens temporariamente

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()
    }
}

Uso:-

truckImageView.loadImageUsingCache(withUrl: currentTruck.logoString)
Jack
fonte
1
Funciona. Apenas copie a remoção do ActivityIndicator em caso de erro também.
djdance
1
muito útil para o meu projeto.
Partikles
13
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)
user3763002
fonte
5
fatal error: unexpectedly found nil while unwrapping an Optional value
Utilizador
Alguma chance de você escrever algo assíncrono?
Jed Grant
@JedGrant você poderia usar dispatch_async para entrar em outro segmento e uma chamada de retorno
Matej
Eu tive que desembrulhar o NSData com um "!" (observe o! no final) para fazê-lo funcionar assim: var imageData: NSData = NSData (contentsOfURL: url, opções: NSDataReadingOptions.DataReadingMappedIfSafe, erro: & err)!
Botbot
13

FYI: Para 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!)
            }
        }
    }
}
Katopz
fonte
5
NSURLConnection está obsoleto. Você precisa usar o NSURLSession. É melhor quando você código com Swift 2.0 e Xcode 7
BilalReffas
11

swift 3 com tratamento de erros

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

With Extension

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

Uso da extensão

myImageView. setCustomImage("url")

Com suporte a cache

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()
        }
    }
}
Manee ios
fonte
9

Swift 4: Um carregador simples para imagens pequenas (ex: miniaturas) que usa o NSCache e sempre roda no thread principal:

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

}

Uso:

ImageLoader.image(for: imageURL) { image in
  self.imageView.image = image
}
fethica
fonte
Por que usar singleton com struct? isso fará com que sua classe para ser imutável, que pode causar problemas com o seu NSCache
XcodeNOOB
Obrigado por apontar isso, eu apenas mudei para class.
fethica 25/03
1
cache de deixar privado estava me dando erro, então mudou para estático e funcionou! Obrigado
Hammad Tariq
7

Você vai querer fazer:

UIImage(data: data)

No Swift, eles substituíram a maioria dos métodos de fábrica do Objective C por construtores regulares.

Vejo:

https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithObjective-CAPIs.html#//apple_ref/doc/uid/TP40014216-CH4-XID_26

joshstaiger
fonte
2
Tecnicamente mapeado, não substituído. Se você criar seu próprio método +(instancetype)[MyThing thingWithOtherThing:], também o chamaria como MyThing(otherThing: ...)no Swift.
Brian Nickel
7

Swift 2 com identificador de erro e cabeçalho de solicitação personalizada

Basta adicionar uma extensão ao 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()
        }
    }
}

E então, use o novo imageFromUrl(urlString: String)para baixar a imagem

Uso:

imageView.imageFromUrl("https://i.imgur.com/ONaprQV.png")
Cody
fonte
no swift 3, continuo recebendo esse erro. Qual é o problema ? Nesta linha "" "URLSession.shared.dataTask (with: url) {" "" "" "" Não é possível invocar 'dataTask' com uma lista de argumentos do tipo '(com: URL, (Data ?, URLResponse ?, Erro? ) -> Void) '
Aymen BRomdhane
7

Swift 4

Este método fará o download de uma imagem de um site de forma assíncrona e a armazenará em cache:

    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()
    }

Em uso:

    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
    }
Bobby
fonte
Como é o cache? E por quanto tempo?
Ramis
Parece estar permanentemente armazenado, o que é estranho, está armazenado na pasta Biblioteca> Caches. Use print (NSHomeDirectory ()) para chegar a esse local no seu computador quando executar no simulador.
Bobby
5

Swift 2.0:

1)

if let url = NSURL(string: "http://etc...") {
    if let data = NSData(contentsOfURL: url) {
        imageURL.image = UIImage(data: data)
    }        
}

OU

imageURL.image =
    NSURL(string: "http:// image name...")
    .flatMap { NSData(contentsOfURL: $0) }
    .flatMap { UIImage(data: $0) }

2) Adicione este método ao VC ou 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)
        }
    }
}

Uso:

self.load_image(" url strig here")
AG
fonte
sendAsynchronousRequest é obsoleto em iOS 9
Crashalot
5

O Kingfisher é uma das melhores bibliotecas para carregar imagem no URL.

URL do 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)
Kamani Jasmin
fonte
5

Aqui está o código de trabalho para carregamento / download de imagem do URL. NSCache automaticamente e exibir imagem de espaço reservado antes de baixar e carregar imagem real (código Swift 4).

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

Use assim:

// 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 }
Nikunj Kumbhani
fonte
1
O único que funcionou comigo. meu problema era que eu estava implementando o music player e queria que a imagem carregasse a notificação quando o telefone também estivesse fechado e o manipulador levasse o tipo de UIImage, então tive que usar esse método.
JhonnyTawk 01/10/19
4

Um método para obter uma imagem segura e que funcione com o Swift 2.0 e o 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)
}

Você chamaria esse método da seguinte maneira:

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.
        }
    }

Se você estiver atualizando a visualização com a imagem, precisará usá-la após o "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
    }

O motivo dessa última parte ser necessária se você estiver usando a imagem na interface do usuário é porque as chamadas de rede demoram. Se você tentar atualizar a interface do usuário usando a imagem sem chamar dispatch_async como acima, o computador procurará a imagem enquanto a imagem ainda está sendo buscada, descobrirá que ainda não há imagem e seguirá em frente como se não houvesse imagem. encontrado. Colocar seu código dentro de um fechamento de conclusão dispatch_async diz ao computador: "Vá, pegue esta imagem e, quando terminar, conclua este código". Dessa forma, você terá a imagem quando o código for chamado e tudo funcionará bem.

Ben Patch
fonte
4

Se você está procurando uma implementação muito, muito simples. (Isso funcionou para mim no Swift 2)

 let imageURL = NSURL(string: "https://farm2.staticflickr.com/1591/26078338233_d1466b7da2_m.jpg")
 let imagedData = NSData(contentsOfURL: imageURL!)!
 imageView?.image = UIImage(data: imagedData)

Eu implementei dentro de uma tableview com uma célula personalizada que tem apenas uma imagem

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

    }
Naishta
fonte
você está certo, é porque o download e a compactação estão acontecendo no thread principal. você deve idealmente fazer estas 2 etapas de forma assíncrona em um segmento de segundo plano, pode estar usando dispatch_async fila mundial
Naishta
Sim, usando o encadeamento em segundo plano, podemos otimizar a velocidade do download e, se necessário, podemos alterar a lógica em vez disso, podemos usar sdWebImage ou outra estrutura.
Gourav Joshi
2

Eu recomendo usar a biblioteca Kingfisher para baixar imagens de forma assíncrona. A melhor parte do uso do Kingfisher é que ele armazena em cache todas as imagens baixadas por padrão, com o URL da imagem como um ID. Da próxima vez que você solicitar o download de uma imagem com esse URl específico, ele será carregado do cache.

Uso:

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()
                }
            })
Raj Salla
fonte
2

Você pode usar o pod SDWebImagepara conseguir o mesmo. É fácil de usar. Você pode obter a documentação aqui SDWebImage

Aqui está o código de exemplo

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)
                }
            })
Shruti Thombre
fonte
2

veloz 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
                    }
                }
            }
        }
    }
}

para usar

override func awakeFromNib() {
    super.awakeFromNib()
    imgView.load(url: "<imageURLHere>")
}
Amjaber
fonte
1

As únicas coisas que faltam são: a!

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!)
Simon Jensen
fonte
Desculpe pela resposta tardia, você pode ser mais preciso sobre os erros que recebe?
Simon Jensen
1

Resposta Swift 2.x que baixa imagem em arquivo (em oposição à resposta de Leo Dabus, que armazena a imagem na memória). Com base na resposta de Leo Dabus e na resposta de Rob de Obtenha os dados de NSURLSession DownloadTaskWithRequest do manipulador de conclusão :

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

}
Crashalot
fonte
1

Swift 4.1 Eu criei uma função apenas passe o URL da imagem, a chave de cache após a imagem ser gerada, configure-a para o bloco de conclusão.

   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()
      })
    }
  }
}
Gurjinder Singh
fonte
1
class func downloadImageFromUrl(with urlStr: String, andCompletionHandler:@escaping (_ result:Bool) -> Void) {
        guard let url = URL(string: urlStr) else {
            andCompletionHandler(false)
            return
        }
        DispatchQueue.global(qos: .background).async {
            URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) -> Void in
                if error == nil {
                    let httpURLResponse = response as? HTTPURLResponse
                    Utils.print( "status code ID : \(String(describing: httpURLResponse?.statusCode))")
                    if httpURLResponse?.statusCode == 200 {
                        if let data = data {
                            if let image = UIImage(data: data) {
                                ImageCaching.sharedInterface().setImage(image, withID: url.absoluteString as NSString)
                                DispatchQueue.main.async {
                                    andCompletionHandler(true)
                                }
                            }else {
                                andCompletionHandler(false)
                            }
                        }else {
                            andCompletionHandler(false)
                        }
                    }else {
                        andCompletionHandler(false)
                    }
                }else {
                    andCompletionHandler(false)
                }
            }).resume()
        }
    }

Eu criei uma função de classe simples na minha Utils.swiftclasse para chamar esse método que você pode simplesmente acessar classname.methodnamee suas imagens são salvas no NSCache usando a ImageCaching.swiftclasse

Utils.downloadImageFromUrl(with: URL, andCompletionHandler: { (isDownloaded) in
                            if isDownloaded {
                                if  let image = ImageCaching.sharedInterface().getImage(URL as NSString) {
                                    self.btnTeam.setBackgroundImage(image, for: .normal)
                                }
                            }else {
                                DispatchQueue.main.async {
                                    self.btnTeam.setBackgroundImage(#imageLiteral(resourceName: "com"), for: .normal)
                                }
                            }
                        })

Feliz codificação. Felicidades:)

Vishal16
fonte
1

Swift 4.2 e AlamofireImage

Se o uso de uma biblioteca não for um problema, você poderá fazê-lo com a ajuda do AlamofireImage. minhas amostras são do Github

Exemplo de imagens de espaço reservado:

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)

possui muitas funções úteis e extensão para trabalhar com imagens. do cache ao dimensionamento e redimensionamento ou até à aplicação de filtros na imagem. se as imagens forem importantes no seu aplicativo, sugiro usar essa estrutura e economizar seu tempo.

Hamid Shahsavari
fonte
0

Usando o Ascyimageview, você pode carregar facilmente o imageurl no imageview.

deixe image1Url: URL = URL (string: "(imageurl)" como String)! imageview.imageURL = image1Url

saurabh rathod
fonte
0

Carregamento de imagem do servidor: -

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()
}

Uso: -

  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)
                        })
                    }
Abhimanyu Rathore
fonte
0

Para obter melhor desempenho UITableViewou UICollectionViewusar a biblioteca de pouco peso Carregamento Preguiçoso Inteligente Você pode usar esta abordagem de carregamento lento se desejar carregar imagens de url Asynchronous

'Carregamento lento' inteligente UICollectionViewou UITableViewusando NSOperatione NSOperationQueueno iOS Portanto, neste projeto, podemos fazer o download de várias imagens em qualquer Visualização ( UICollectionViewou UITableView) otimizando o desempenho de um aplicativo usando Operatione OperationQueuepor simultaneidade. A seguir, estão o ponto principal deste projeto Smart Lazy Loading: Criando serviço de download de imagens. Priorize o download com base na visibilidade das células.

A classe ImageDownloadService criará uma instância singleton e terá uma instância NSCache para armazenar em cache as imagens que foram baixadas. Nós herdamos a classe Operation para TOperation para alterar a funcionalidade de acordo com nossa necessidade. Penso que as propriedades da subclasse de operação são bastante claras em termos de funcionalidade. Estamos monitorando as mudanças de estado das operações usando o KVO.

jawadAli
fonte