Ładowanie / pobieranie obrazu z adresu URL w Swift


412

Chciałbym załadować obraz z adresu URL w mojej aplikacji, więc najpierw spróbowałem z Objective-C i działało to jednak w Swift, mam błąd kompilacji:

Opcja „imageWithData” jest niedostępna: użyj konstrukcji obiektu „UIImage (data :)”

Moja funkcja:

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

W celu 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";
 }

Czy ktoś może mi wyjaśnić, dlaczego imageWithData:nie działa ze Swift i jak mogę rozwiązać problem.


3
Spróbuj tegoimageURL.image = UIImage(data: myDataVar)
aleclarson,

Idealnie, działało! Dziękuję Jednak nie wiem, dlaczego ta metoda działa w Celu C, a nie w
Szybkim

Jeśli masz problemy z klasą Cocoa, spróbuj CMD + kliknij nazwę klasy, a powinieneś zobaczyć interfejs Swift dla klasy!
aleclarson

3
if let url = NSURL (string: "imageurl") {if let data = NSData (contentOfURL: url) {imageView.image = UIImage (data: data)}}
Hardik Bar

2
@Leo Dabus To pytanie nie jest specyficzne dla Swift 2. Przestań dodawać do niego ten tag.
Mick MacCallum,

Odpowiedzi:


769

Xcode 8 lub nowszy • Swift 3 lub nowszy

Synchronicznie:

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

Asynchronicznie:

Utwórz metodę za pomocą modułu obsługi zakończenia, aby uzyskać dane obrazu z adresu URL

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

Utwórz metodę pobierania obrazu (rozpocznij zadanie)

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

Stosowanie:

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

Przedłużenie :

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

Stosowanie:

imageView.downloaded(from: "https://cdn.arstechnica.net/wp-content/uploads/2018/06/macOS-Mojave-Dynamic-Wallpaper-transition.jpg")

13
Tylko uwaga tutaj, powinieneś ustawić do niej powiązany obiekt; W przeciwnym razie możesz ładować obrazy jeden na drugim. Np. W miejscu, w UITableViewktórym komórka pokazuje obraz, a obraz jest aktualizowany po zwróceniu odmalowanej komórki. Jeśli proces nr 1 trwa dłużej niż proces nr 2, proces nr 2 wyświetli jego obraz, a następnie zostanie zaktualizowany przez proces nr 1, nawet jeśli ten obraz nie jest już ważny dla użytkownika.
Paul Peelen,

1
To rozwiązało mój problem. Kiedy próbuję załadować obraz z adresem URL w widoku tabeli, widok przewijania nie reaguje podczas przewijania w górę lub w dół. Użyłem rozszerzenia UIImageView i to rozwiązało mój problem. Dzięki.
JengGe Chao

1
@LeoDabus ok dzięki. i dziękuję za udzielenie odpowiedzi na tak wiele pytań dotyczących SO! jesteś jak profesor szybkiego i iOS. :)
Crashalot,

2
@LeoDabus, dlaczego użyłeś dataTaskWithURL zamiast downloadTaskWithURL?
Crashalot,

3
Pierwszy pobiera do pamięci drugi do pliku
Leo Dabus 20.04.16

349

(Aktualizacja Swift 4) Aby bezpośrednio odpowiedzieć na oryginalne pytanie, oto szybki odpowiednik opublikowanego fragmentu Celu-C.

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

ZRZECZENIE SIĘ:

Ważne jest, aby pamiętać, że Data(contentsOf:)metoda pobierze zawartość adresu URL synchronicznie w tym samym wątku, w którym wykonywany jest kod, więc nie wywołuj tego w głównym wątku aplikacji.

Prostym sposobem na asynchroniczne uruchamianie tego samego kodu bez blokowania interfejsu użytkownika jest użycie 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!)
    }
}

To powiedziawszy, w rzeczywistych aplikacjach, jeśli chcesz mieć najlepszą jakość obsługi i uniknąć wielokrotnego pobierania tego samego obrazu, możesz chcieć mieć je nie tylko pobrane, ale także buforowane. Jest już sporo bibliotek, które działają bardzo płynnie i wszystkie są naprawdę łatwe w użyciu. Osobiście polecam 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) 

I to wszystko


12
„Łatwo” jest subiektywne, a początkujący koderzy nie oczekują tego niechcianego zachowania, ale skopiuj / wklej to wycięte tak, jak jest. Być może możesz zaktualizować swoją odpowiedź?
Orlin Georgiev

13
Nadal się nie zgadzam. Niektóre odpowiedzi powinny być proste, wiele osób, które tu przychodzą, lubi kopiować i wklejać małe fragmenty kodu. Właśnie przetłumaczyłem na Swift kod celu C z pierwotnego pytania, wszystko oprócz tego jest bonusem. Nawiasem mówiąc, odpowiedź na to samo pytanie zawiera już taką odpowiedź, która nie ma sensu powielać informacji.
Lucas Eduardo

1
@ Użytkownik po prostu zamień na image.urldowolny ciąg adresu URL, na stałe lub nie :)
Lucas Eduardo

1
@IanWarburton Oczywiście możesz użyć, co chcesz :). 1) Ta odpowiedź jest oparta na pierwotnym pytaniu, które wykorzystywało to samo podejście w celu C, więc pomogłem w „tłumaczeniu” na szybkie. 2) Usunięcie tego podejścia nie ma sensu, URLSession.dataTaskponieważ wiele innych odpowiedzi tutaj już pokazuje, jak to zrobić, lepiej pozostawić różne opcje otwarte.
Lucas Eduardo,

2
Heyo, sugerowane ramy, zimorodek, jak podano w odpowiedzi, wykona wszystkie ciężkie podnoszenie. Dzieki za sugestie!
Kilmazing

68

Jeśli chcesz tylko załadować obraz (asynchronicznie!) - po prostu dodaj to małe rozszerzenie do szybkiego kodu:

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

I użyj tego w ten sposób:

myImageView.imageFromUrl("https://robohash.org/123.png")

1
Nie pokazuje się i myślę, że istnieje potrzeba GCD. Jak mogę odświeżyć?
jo3birdtalk

@ jo3birdtalk to nie jest problem z GCD. Sprawdź swoje powiązania z widokiem.
skywinder

6
NSURLConnection jest przestarzały w iOS 9 i tak dalej. Użyj intur NSURLSession.
muhasturk

2
sendAsynchronousRequest jest przestarzałe w iOS 9
Crashalot 20.04.2016

Dzięki skywinder, używam tej metody do pobierania obrazów z tablicy. Chcę, gdy użytkownik naciśnie cancelprzycisk, aby zatrzymać pobieranie. Czy masz pomysł, jak to zrobić za pomocą tej metody? Muszę dodać funkcję anulowania.
ZAFAR007

44

Szybki 2,2 || Xcode 7.3

Mam niesamowite wyniki !! z szybką biblioteką AlamofireImage

Zapewnia wiele funkcji, takich jak:

  • Pobieranie asynchroniczne
  • Automatyczne czyszczenie pamięci podręcznej obrazu, jeśli pojawią się ostrzeżenia pamięci dla aplikacji
  • Buforowanie adresu URL obrazu
  • Buforowanie obrazu
  • Unikaj podwójnego pobierania

i bardzo łatwe do wdrożenia dla Twojej aplikacji

Krok 1 Zainstaluj strąki


Alamofire 3.3.x

pod „Alamofire”

AlamofireImage 2.4.x

pod „AlamofireImage”

Krok 2 zaimportuj i użyj

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)

Otóż ​​to!! zajmie się wszystkim


Ogromne podziękowania dla facetów Alamofire za ułatwienie życia iDevelopers;)


8
Swift 3: foregroundImage.af_setImage (withURL: downloadURL as URL)
thejuki 12.10.16

28

Xcode 8Swift 3

Odpowiedź Leo Dabusa jest niesamowita! Chciałem tylko zapewnić kompleksowe rozwiązanie funkcji:

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

3
Mark, prawdopodobnie oznaczał na końcu „asynchronię”
Fattie

16

Zawarłem kod najlepszych odpowiedzi na pytanie w pojedynczą klasę wielokrotnego użytku rozszerzającą UIImageView, abyś mógł bezpośrednio używać asynchronicznego ładowania UIImageView w swojej scenorysie (lub utworzyć je z kodu).

Oto moja klasa:

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

a oto jak go użyć:

imageView.downloadImage("http://www.image-server.com/myImage.jpg")

2
Czy naprawdę potrzebujemy tych przesłoniętych obiektów? Zresztą odziedziczone rzeczy? Wygląda na to, że po prostu tu nadpisujemy, żeby nazywać się super, co wydaje mi się zbędne.
Inżynier NYC Tech,

16

Swift 4 ::

Spowoduje to wyświetlenie programu ładującego podczas ładowania obrazu. Możesz użyć NSCache, które tymczasowo przechowują obraz

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

Stosowanie:-

truckImageView.loadImageUsingCache(withUrl: currentTruck.logoString)

1
To działa. Po prostu skopiuj usunięcie activityIndicator również w przypadku błędu.
djdance 21.04.19

1
bardzo przydatne w moim projekcie.
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)

5
fatal error: unexpectedly found nil while unwrapping an Optional value
Użytkownik

Czy jest jakaś szansa, że ​​możesz napisać coś asynchronicznego?
Jed Grant,

@JedGrant możesz użyć dispatch_async, aby przejść do innego wątku i oddzwonienia
Matej,

Musiałem rozpakować dane NSD za pomocą „!” (zwróć uwagę na! na końcu), aby działało w następujący sposób: var imageData: NSData = NSData (contentOfURL: url, opcje: NSDataReadingOptions.DataReadingMappedIfSafe, błąd: i err)!
botbot

13

FYI: Dla 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
NSURLConnection jest depracowany. Musisz użyć NSURLSession. Lepiej jest, gdy
kodujesz za

10

swift 3 z obsługą błędów

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

Z przedłużeniem

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

Wykorzystanie rozszerzenia

myImageView. setCustomImage("url")

Z obsługą pamięci podręcznej

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

Swift 4: Prosty moduł ładujący dla małych obrazów (np. Miniatur), który używa NSCache i zawsze działa w głównym wątku:

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

}

Stosowanie:

ImageLoader.image(for: imageURL) { image in
  self.imageView.image = image
}

Dlaczego warto korzystać z singletonu ze strukturą? spowoduje to, że twoja klasa będzie niezmienna, co może powodować problemy z twoją pamięcią NSCache
XcodeNOOB

Dzięki za zwrócenie na to uwagi, właśnie to zmieniłem na class.
fethica

1
private let cache dawał mi błąd, więc zmieniłem go na statyczny i działało! Dzięki
Hammad Tariq

7

Będziesz chciał zrobić:

UIImage(data: data)

W Swift zastąpiły większość metod fabrycznych Celu C zwykłymi konstruktorami.

Widzieć:

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


2
Odwzorowany technicznie, nie zastąpiony. Jeśli stworzysz własną metodę +(instancetype)[MyThing thingWithOtherThing:], nazwałbyś ją tak jak MyThing(otherThing: ...)Swift.
Brian Nickel

7

Swift 2 z uchwytem błędu i niestandardowym nagłówkiem żądania

Po prostu dodaj rozszerzenie do 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()
        }
    }
}

A następnie użyj nowego, imageFromUrl(urlString: String)aby pobrać obraz

Stosowanie:

imageView.imageFromUrl("https://i.imgur.com/ONaprQV.png")

w swift 3 ciągle pojawia się ten błąd. Jaki jest problem ? W tym wierszu „” „URLSession.shared.dataTask (with: url) {" „„ „” „” „Nie można wywołać„ dataTask ”z listą argumentów typu” (z: URL, (Dane ?, URLResponse ?, Błąd? ) -> Void) ”
Aymen BRomdhane

7

Szybki 4

Ta metoda pobierze obraz ze strony asynchronicznie i buforuje go:

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

W użyciu:

    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
    }

Jak to jest buforowanie? I na jak długo?
Ramis

Wygląda na to, że jest trwale przechowywany, co jest dziwne, jest przechowywany w folderze Biblioteka> Pamięć podręczna. Użyj print (NSHomeDirectory ()), aby dostać się do tej lokalizacji na komputerze, gdy jest uruchomiony na symulatorze.
Bobby

5

Swift 2.0:

1)

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

LUB

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

2) Dodaj tę metodę do VC lub 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)
        }
    }
}

Stosowanie :

self.load_image(" url strig here")

sendAsynchronousRequest jest przestarzałe w iOS 9
Crashalot 20.04.2016

5

Kingfisher to jedna z najlepszych bibliotek do ładowania obrazu do adresu 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)

5

Oto działający kod do ładowania / pobierania obrazu z adresu URL. NSCache automatycznie i wyświetl obraz zastępczy przed pobraniem i załaduj rzeczywisty obraz (kod 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!
                        }
                    }
                }
            }
        }
    }
}

Użyj w ten sposób:

// 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
Jedyne, które ze mną działało. mój problem polegał na tym, że wdrażałem odtwarzacz muzyki i chciałem, aby obraz załadował się w powiadomieniu, gdy telefon jest również zamknięty, a program obsługi przyjmuje typ UIImage, więc musiałem użyć tej metody.
JhonnyTawk

4

Metoda uzyskania obrazu, który jest bezpieczny i działa z Swift 2.0 i 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)
}

Następnie wywołaj tę metodę w następujący sposób:

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

Jeśli aktualizujesz widok obrazem, będziesz musiał użyć tego po „if sukces {”:

    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
    }

Ta ostatnia część jest potrzebna, jeśli używasz obrazu w interfejsie użytkownika, ponieważ połączenia sieciowe wymagają czasu. Jeśli spróbujesz zaktualizować interfejs użytkownika za pomocą obrazu bez wywoływania metody dispatch_async, jak wyżej, komputer będzie szukał obrazu podczas pobierania obrazu, stwierdzi, że nie ma go (jeszcze), i przejdzie tak, jakby nie było obrazu znaleziony. Umieszczenie kodu w zamknięciu zakończenia dispatch_async mówi komputerowi: „Idź, weź ten obraz, a kiedy skończysz, wypełnij ten kod”. W ten sposób będziesz mieć obraz, gdy kod zostanie wywołany i wszystko będzie działać dobrze.


4

Jeśli szukasz bardzo, bardzo prostej implementacji. (To działało dla mnie w 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)

Zaimplementowałem w widoku tabeli z niestandardową komórką, która ma tylko obraz

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

    }

masz rację, ponieważ pobieranie i kompresja odbywa się w głównym wątku. najlepiej wykonać te 2 kroki asynchronicznie w wątku w tle, może być używana globalna kolejka
dispatch_async

Tak, używając wątku w tle możemy zoptymalizować szybkość pobierania, a jeśli to konieczne, możemy zmienić logikę zamiast tego możemy użyć sdWebImage lub innego frameworka.
Gourav Joshi

2

Polecam korzystanie z biblioteki Kingfisher do asynchronicznego pobierania zdjęć. Najlepszą częścią korzystania z Kingfishera jest to, że domyślnie buforuje wszystkie pobrane obrazy z adresem URL obrazu jako identyfikatorem. Następnym razem, gdy poprosisz o pobranie obrazu z tym konkretnym UR1, załaduje go z pamięci podręcznej.

Stosowanie:

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

2

Możesz użyć kapsułki, SDWebImageaby osiągnąć to samo. Jest łatwy w użyciu. Tutaj możesz uzyskać dokumentację SDWebImage

Oto przykładowy kod

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

1

Brakuje tylko jednej rzeczy!

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

Przepraszam za późną odpowiedź. Czy możesz być bardziej precyzyjny w zakresie błędów?
Simon Jensen

1

Odpowiedź Swift 2.x, która pobiera obraz do pliku (w przeciwieństwie do odpowiedzi Leo Dabusa, która przechowuje obraz w pamięci). Na podstawie odpowiedzi Leo Dabusa i Roba z Pobierz dane z NSURLSession DownloadTaskWithRequest z modułu obsługi zakończenia :

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

}

1

Swift 4.1 Stworzyłem funkcję, po prostu przekazuję adres URL obrazu, klucz pamięci podręcznej po wygenerowaniu obrazu ustaw go na blok zakończenia.

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

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

W mojej Utils.swiftklasie utworzyłem prostą funkcję klasy do wywoływania tej metody, do której można łatwo uzyskać dostęp, classname.methodnamea obrazy są zapisywane w pamięci NSCache za pomocą ImageCaching.swiftklasy

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

Happy Codding. Twoje zdrowie:)


1

Swift 4.2 i AlamofireImage

Jeśli korzystanie z biblioteki nie stanowi problemu, możesz to zrobić za pomocą AlamofireImage. moje próbki pochodzą z Github

Przykład symboli zastępczych:

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)

ma wiele przydatnych funkcji i rozszerzeń do pracy z obrazami. od buforowania po skalowanie i zmianę rozmiaru, a nawet stosowanie filtrów na obrazie. jeśli obrazy są ważne w Twojej aplikacji, sugeruję skorzystanie z tej struktury i oszczędność czasu.


1

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

za korzystanie

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

0

Korzystając z Ascyimageview możesz łatwo załadować imageurl do imageview.

niech image1Url: URL = URL (ciąg: „(imageurl)” jako ciąg)! imageview.imageURL = image1Url


0

Ładowanie obrazu z serwera: -

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

Stosowanie :-

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

0

Dla lepszej wydajności UITableViewlub UICollectionViewkorzystania z lekkiej biblioteki Inteligentne leniwe ładowanie Możesz użyć tego leniwego ładowania, jeśli chcesz ładować obrazy z adresu asynchronicznego

Inteligentne „Leniwe ładowanie” do UICollectionViewlub UITableViewza pomocą NSOperationi NSOperationQueuena iOS Więc w tym projekcie możemy pobrać wiele obrazów w dowolnym widoku ( UICollectionViewlub UITableView), optymalizując wydajność aplikacji za pomocą OperationiOperationQueue dla współbieżności. Oto kluczowe punkty tego projektu Smart Lazy Loading: Tworzenie usługi pobierania obrazu. Określ priorytet pobierania na podstawie widoczności komórek.

Klasa ImageDownloadService utworzy instancję singleton i będzie miała instancję NSCache do buforowania pobranych obrazów. Odziedziczyliśmy klasę Operation do TOperation, aby dostosować funkcjonalność do naszych potrzeb. Myślę, że właściwości podklasy operacji są dość jasne pod względem funkcjonalności. Monitorujemy zmiany operacyjne operacji za pomocą KVO.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.