กำลังโหลด / ดาวน์โหลดภาพจาก URL บน Swift


413

ฉันต้องการโหลดภาพจาก 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
}

ในวัตถุประสงค์ -C:

- (void)viewDidLoad {
    [super viewDidLoad];

    NSURL *url = [NSURL URLWithString:(@"http://myURL/ios8.png")];
    NSData *data = [NSData dataWithContentsOfURL:url];

    _imageView.image = [UIImage imageWithData: data];
    _labelURL.text = @"http://www.quentinroussat.fr/assets/img/iOS%20icon's%20Style/ios8.png";
 }

ใครช่วยอธิบายหน่อยได้ไหมว่าทำไมมันถึงimageWithData:ไม่ทำงานกับ Swift และฉันจะแก้ปัญหาได้อย่างไร


3
ลองสิ่งนี้imageURL.image = UIImage(data: myDataVar)
aleclarson

มันทำงานได้สมบูรณ์แบบ! ขอบคุณอย่างไรก็ตามฉันไม่ทำไมวิธีการนี้ใช้งานได้ใน Objective C และไม่ใช่ Swift ...
Str

เมื่อคุณมีปัญหากับคลาส Cocoa ให้ลอง CMD + คลิกชื่อชั้นเรียนและคุณควรเห็นอินเทอร์เฟซ Swift สำหรับชั้นเรียน!
aleclarson

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

2
@Leo Dabus คำถามนี้ไม่เฉพาะเจาะจงสำหรับ Swift 2 โปรดหยุดเพิ่มแท็กนั้น
มิค MacCallum

คำตอบ:


769

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

การใช้งาน:

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

ส่วนขยาย :

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

การใช้งาน:

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

13
แค่ด้านข้างที่นี่คุณควรตั้งวัตถุที่เกี่ยวข้องให้ มิฉะนั้นคุณอาจโหลดภาพที่อยู่ด้านบนของกันและกัน เช่นในUITableViewที่ที่เซลล์แสดงภาพและภาพจะได้รับการอัพเดตเมื่อคืนเซลล์ที่ถูกตัดออก หากกระบวนการ # 1 ใช้เวลานานกว่ากระบวนการ # 2 กระบวนการ # 2 จะแสดงอิมเมจและกระบวนการนั้นจะถูกอัพเดตในภายหลังโดยกระบวนการ # 1 แม้ว่ารูปภาพนั้นจะไม่สามารถใช้งานได้สำหรับผู้ใช้อีกต่อไป
Paul Peelen

1
สิ่งนี้แก้ไขปัญหาของฉันได้ เมื่อฉันพยายามโหลดภาพโดยใช้ url ในมุมมองตารางมุมมองเลื่อนจะไม่ตอบสนองเมื่อเลื่อนขึ้นหรือลง ฉันใช้ส่วนขยาย UIImageView และแก้ไขปัญหาของฉัน ขอบคุณ
JengGe Chao

1
@LeoDabus โอเคขอบคุณ และขอบคุณสำหรับการตอบคำถามมากมายใน SO! คุณเป็นเหมือนศาสตราจารย์แห่งความรวดเร็วและ iOS :)
Crashalot

2
@LeoDabus ทำไมคุณถึงใช้ dataTaskWithURL แทน downloadTaskWithURL
Crashalot

3
คนแรกที่ดาวน์โหลดไปยังหน่วยความจำคนที่สองไปยังไฟล์
Leo Dabus

349

(อัปเดต Swift 4) เพื่อตอบคำถามเดิมโดยตรงนี่คือตัวอย่างรวดเร็วของ Snive Objective-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!)

หมายเหตุ:

เป็นสิ่งสำคัญที่จะต้องทราบว่าData(contentsOf:)วิธีการจะดาวน์โหลดเนื้อหาของ url แบบซิงโครนัสในเธรดเดียวกับที่โค้ดถูกเรียกใช้ดังนั้นอย่าเรียกใช้สิ่งนี้ในเธรดหลักของแอปพลิเคชันของคุณ

วิธีง่ายๆในการทำให้รหัสเดียวกันทำงานแบบอะซิงโครนัสไม่ใช่การบล็อก UI คือการใช้ GCD:

let url = URL(string: image.url)

DispatchQueue.global().async {
    let data = try? Data(contentsOf: url!) //make sure your image in this url does exist, otherwise unwrap in a if let check / try-catch
    DispatchQueue.main.async {
        imageView.image = UIImage(data: data!)
    }
}

ดังที่กล่าวไว้ในแอปพลิเคชันในชีวิตจริงหากคุณต้องการประสบการณ์การใช้งานที่ดีที่สุดและหลีกเลี่ยงการดาวน์โหลดภาพเดียวกันหลายครั้งคุณอาจต้องการดาวน์โหลดไม่เพียง แต่ดาวน์โหลดเท่านั้น มีห้องสมุดอยู่ไม่กี่แห่งที่สามารถดำเนินการได้อย่างราบรื่นและใช้งานง่ายมาก ฉันแนะนำKingfisher :

import Kingfisher

let url = URL(string: "url_of_your_image")
// this downloads the image asynchronously if it's not cached yet
imageView.kf.setImage(with: url) 

และนั่นคือมัน


12
"ง่ายดาย" เป็นแบบอัตนัยและผู้ใช้งานมือใหม่จะไม่คาดหวังพฤติกรรมที่ไม่พึงประสงค์นี้ แต่คัดลอก / วางสแนปอินอย่างที่มันเป็น บางทีคุณสามารถปรับปรุงคำตอบของคุณ?
Orlin Georgiev

13
ฉันยังไม่เห็นด้วย คำตอบบางอย่างควรเป็นเรื่องง่ายผู้คนจำนวนมากที่มาที่นี่ชอบคัดลอกและวางโค้ดขนาดเล็ก ฉันเพิ่งแปลรหัสวัตถุประสงค์ -C ของ Swift จากคำถามเดิมทุกอย่างนอกเหนือจากนั้นคือโบนัส และโดยวิธีการที่มีคำตอบในคำถามเดียวกันนี้ให้ข้อมูลโบนัสชนิดนี้ซึ่งทำให้รู้สึกน้อยลงในการทำซ้ำข้อมูล
Lucas Eduardo

1
@User เพียงแทนที่image.urlด้วย URL สตริงใด ๆ hardcoded หรือไม่ :)
ลูคัส Eduardo

1
@IanWarburton แน่นอนคุณสามารถใช้สิ่งที่คุณต้องการ :) 1) คำตอบนี้ตั้งอยู่ในคำถามเดิมซึ่งใช้วิธีเดียวกันในวัตถุประสงค์ -C ดังนั้นฉันจึงช่วยในการ "แปล" เพื่อรวดเร็ว 2) ไม่เหมาะสมเลยที่จะลบวิธีการนี้และวางไว้URLSession.dataTaskเพราะมีคำตอบอื่น ๆ อีกมากมายที่นี่แสดงให้เห็นแล้วว่าจะทำอย่างไรดีกว่าที่จะเปิดตัวเลือกต่าง ๆ ไว้
Lucas Eduardo

2
Heyo กรอบที่แนะนำกระเต็นตามที่ระบุไว้ในคำตอบจะทำทุกอย่างของการยกของหนัก ขอบคุณสำหรับคำแนะนำ!
Kilmazing

68

หากคุณต้องการโหลดภาพ(แบบอะซิงโครนัส!) - เพียงเพิ่มส่วนขยายเล็ก ๆ นี้ในรหัสรวดเร็วของคุณ:

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
มันไม่ปรากฏขึ้นและฉันคิดว่าเพราะมีความต้องการ GCD ฉันจะรีเฟรชได้อย่างไร
jo3birdtalk

@ jo3birdtalk ไม่ใช่ปัญหาของ GCD ตรวจสอบการเชื่อมโยงของคุณด้วยมุมมอง
skywinder

6
NSURLConnection เลิกใช้งานบน iOS 9 และต่อไป ใช้การบุกรุกของ NSURLS
muhasturk

2
sendAsynchronousRequest เลิกใช้แล้วใน iOS 9
Crashalot

ขอบคุณ skywinder ฉันใช้วิธีนี้ในการดาวน์โหลดภาพจากแถวลำดับ ฉันต้องการเมื่อผู้ใช้กดcancelปุ่มมันหยุดดาวน์โหลด คุณมีความคิดใดบ้างที่ฉันสามารถทำได้ด้วยการใช้วิธีนี้? ฉันต้องเพิ่มฟังก์ชั่นการยกเลิก
ZAFAR007

44

Swift 2.2 || Xcode 7.3

ฉันได้รับผลลัพธ์ที่น่าอัศจรรย์ !! กับAlamofireImage swift library

มันมีคุณสมบัติหลายอย่างเช่น:

  • ดาวน์โหลดแบบอะซิงโครนัส
  • ล้างรูปภาพแคชอัตโนมัติหากมีคำเตือนหน่วยความจำเกิดขึ้นสำหรับแอป
  • แคช URL รูปภาพ
  • แคชรูปภาพ
  • หลีกเลี่ยงการดาวน์โหลดซ้ำ

และใช้งานง่ายมากสำหรับแอปของคุณ

ขั้นตอนที่ 1 ติดตั้งพ็อด


Alamofire 3.3.x

พ็อด 'Alamofire'

AlamofireImage 2.4.x

พ็อด '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 ใช้ชีวิตได้ง่าย;)


8
Swift 3: foregroundImage.af_setImage (withURL: downloadURL เป็น URL)
thejuki

28

Xcode 8Swift 3

คำตอบของ Leo Dabus นั้นยอดเยี่ยมมาก! ฉันแค่อยากจะมอบโซลูชันฟังก์ชั่นแบบครบวงจร:

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
มาร์คอาจหมายถึง "async" ในตอนท้ายนั่น
Fattie

16

ฉันรวบรวมโค้ดของคำตอบที่ดีที่สุดสำหรับคำถามไว้ในคลาสที่สามารถนำมาใช้ซ้ำได้ซึ่งขยาย UIImageView ดังนั้นคุณจึงสามารถใช้การโหลด UIImageViews แบบอะซิงโครนัสโดยตรงในกระดานเรื่องราวของคุณ (หรือสร้างจากรหัส)

นี่คือชั้นเรียนของฉัน:

import Foundation
import UIKit

class UIImageViewAsync :UIImageView
{

    override init()
    {
        super.init(frame: CGRect())
    }

    override init(frame:CGRect)
    {
        super.init(frame:frame)
    }

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    func getDataFromUrl(url:String, completion: ((data: NSData?) -> Void)) {
        NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: url)!) { (data, response, error) in
            completion(data: NSData(data: data))
        }.resume()
    }

    func downloadImage(url:String){
        getDataFromUrl(url) { data in
            dispatch_async(dispatch_get_main_queue()) {
                self.contentMode = UIViewContentMode.ScaleAspectFill
                self.image = UIImage(data: data!)
            }
        }
    }
}

และนี่คือวิธีการใช้งาน:

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

2
เราจำเป็นต้องมี inits แทนที่เหล่านั้นจริง ๆ หรือไม่ ยังไม่ได้รับการสืบทอดมาก่อนหรือไม่ ดูเหมือนว่าพวกเรากำลังจะเอาชนะที่นี่เพียงเพื่อเรียกตัวเองว่าสุดยอดซึ่งทำให้ฉันรู้สึกซ้ำซ้อน
NYC Tech Engineer

16

สวิฟท์ 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)

1
มันได้ผล. เพียงแค่คัดลอกการลบกิจกรรมตัวบ่งชี้ในกรณีที่เกิดข้อผิดพลาดด้วย
djdance

1
มีประโยชน์มากสำหรับโครงการของฉัน
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
ผู้ใช้

โอกาสใด ๆ ที่คุณสามารถเขียนสิ่งที่ไม่ตรงกัน?
Jed Grant

@JedGrant คุณสามารถใช้ dispatch_async เพื่อเข้าสู่เธรดอื่นและการโทรกลับ
Matej

ฉันต้องแกะ NSData ด้วย "!" (โปรดทราบ! ในตอนท้าย) เพื่อให้มันทำงานเช่นนี้: var imageData: NSData = NSData (contentOfURL: url, ตัวเลือก: NSDataReadingOptions.DataReadingMapped ifSafe ข้อผิดพลาด: & err)!
botbot

13

FYI: สำหรับ 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 ไม่ได้ใช้งาน คุณต้องใช้ NSURLSession จะดีกว่าเมื่อคุณใช้รหัสกับ Swift 2.0 และ Xcode 7
BilalReffas

10

swift 3 พร้อมจัดการข้อผิดพลาด

let url = URL(string: arr[indexPath.row] as! String)
if url != nil {
    DispatchQueue.global().async {
        let data = try? Data(contentsOf: url!) //make sure your image in this url does exist, otherwise unwrap in a if let check / try-catch
        DispatchQueue.main.async {
            if data != nil {
                cell.imgView.image = UIImage(data:data!)
            }else{
                cell.imgView.image = UIImage(named: "default.png")
            }
        }
    }
}

พร้อมนามสกุล

extension UIImageView {

    func setCustomImage(_ imgURLString: String?) {
        guard let imageURLString = imgURLString else {
            self.image = UIImage(named: "default.png")
            return
        }
        DispatchQueue.global().async { [weak self] in
            let data = try? Data(contentsOf: URL(string: imageURLString)!)
            DispatchQueue.main.async {
                self?.image = data != nil ? UIImage(data: data!) : UIImage(named: "default.png")
            }
        }
    }
}

การใช้งานส่วนขยาย

myImageView. setCustomImage("url")

ด้วยการสนับสนุนแคช

let imageCache = NSCache<NSString, UIImage>()

extension UIImageView {

    func loadImageUsingCacheWithURLString(_ URLString: String, placeHolder: UIImage?) {

        self.image = nil
        if let cachedImage = imageCache.object(forKey: NSString(string: URLString)) {
            self.image = cachedImage
            return
        }

        if let url = URL(string: URLString) {
            URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in

                //print("RESPONSE FROM API: \(response)")
                if error != nil {
                    print("ERROR LOADING IMAGES FROM URL: \(String(describing: error))")
                    DispatchQueue.main.async { [weak self] in
                        self?.image = placeHolder
                    }
                    return
                }
                DispatchQueue.main.async { [weak self] in
                    if let data = data {
                        if let downloadedImage = UIImage(data: data) {
                            imageCache.setObject(downloadedImage, forKey: NSString(string: URLString))
                            self?.image = downloadedImage
                        }
                    }
                }
            }).resume()
        }
    }
}

9

Swift 4:ตัวโหลดอย่างง่ายสำหรับรูปภาพขนาดเล็ก (เช่นภาพขนาดย่อ) ที่ใช้ NSCache และทำงานบนเธรดหลักเสมอ:

class ImageLoader {

  private static let cache = NSCache<NSString, NSData>()

  class func image(for url: URL, completionHandler: @escaping(_ image: UIImage?) -> ()) {

    DispatchQueue.global(qos: DispatchQoS.QoSClass.background).async {

      if let data = self.cache.object(forKey: url.absoluteString as NSString) {
        DispatchQueue.main.async { completionHandler(UIImage(data: data as Data)) }
        return
      }

      guard let data = NSData(contentsOf: url) else {
        DispatchQueue.main.async { completionHandler(nil) }
        return
      }

      self.cache.setObject(data, forKey: url.absoluteString as NSString)
      DispatchQueue.main.async { completionHandler(UIImage(data: data as Data)) }
    }
  }

}

การใช้งาน:

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

ทำไม่ต้องใช้บริการกับ singleton กับ struct? สิ่งนี้จะทำให้คลาสของคุณไม่เปลี่ยนรูปซึ่งอาจทำให้เกิดปัญหากับ NSCache ของคุณ
XcodeNOOB

classขอขอบคุณที่ชี้ออกมานี้ผมได้เปลี่ยนเพียงแค่มัน
fethica

1
แคชให้ส่วนตัวทำให้ฉันมีข้อผิดพลาดดังนั้นเปลี่ยนเป็นแบบคงที่และใช้งานได้! ขอบคุณ
Hammad Tariq

7

คุณจะต้องทำ:

UIImage(data: data)

ใน Swift พวกเขาได้เปลี่ยนวิธีการในโรงงาน Objective C ส่วนใหญ่เป็น constructors ปกติ

ดู:

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


2
แมปทางเทคนิคไม่ถูกแทนที่ หากคุณทำวิธีการของคุณเอง+(instancetype)[MyThing thingWithOtherThing:]คุณจะเรียกมันว่าMyThing(otherThing: ...)ในสวิฟท์เช่นกัน
Brian Nickel

7

Swift 2 พร้อมข้อผิดพลาดหมายเลขอ้างอิงและส่วนหัวคำขอที่กำหนดเอง

เพียงเพิ่มส่วนขยายไปยัง UIImageView:

extension UIImageView {
    public func imageFromUrl(urlString: String) {
        if let url = NSURL(string: urlString) {
            let request = NSMutableURLRequest(URL: url)
            request.setValue("<YOUR_HEADER_VALUE>", forHTTPHeaderField: "<YOUR_HEADER_KEY>")
            NSURLSession.sharedSession().dataTaskWithRequest(request) {
                (data, response, error) in
                guard let data = data where error == nil else{
                    NSLog("Image download error: \(error)")
                    return
                }

                if let httpResponse = response as? NSHTTPURLResponse{
                    if httpResponse.statusCode > 400 {
                        let errorMsg = NSString(data: data, encoding: NSUTF8StringEncoding)
                        NSLog("Image download error, statusCode: \(httpResponse.statusCode), error: \(errorMsg!)")
                        return
                    }
                }

            dispatch_async(dispatch_get_main_queue(), {
                NSLog("Image download success")
                self.image = UIImage(data: data)
            })
            }.resume()
        }
    }
}

จากนั้นใช้ใหม่ imageFromUrl(urlString: String)รูปภาพเพื่อดาวน์โหลด

การใช้งาน:

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

ใน swift 3 ฉันได้รับข้อผิดพลาดนี้ต่อไป มีปัญหาอะไร ? ในบรรทัดนี้ "" "URLSession.shared.dataTask (with: url) {" "" "" "ไม่สามารถเรียกใช้ 'dataTask' พร้อมกับรายการอาร์กิวเมนต์ประเภท '(พร้อม: URL, (Data ?, URLResponse ?, ข้อผิดพลาด? ) -> เป็นโมฆะ) '
Aymen BRomdhane

7

สวิฟต์ 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
    }

มันเป็นแคชอย่างไร และนานเท่าไหร่?
Ramis

ดูเหมือนว่าจะถูกจัดเก็บอย่างถาวรซึ่งเป็นเรื่องแปลกมันถูกเก็บไว้ในโฟลเดอร์ Library> Caches ใช้ print (NSHomeDirectory ()) เพื่อไปยังตำแหน่งนี้ในคอมพิวเตอร์ของคุณเมื่อเรียกใช้โปรแกรมจำลอง
Bobby

5

Swift 2.0:

1)

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

หรือ

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

2) เพิ่มวิธีนี้ใน VC หรือส่วนขยาย

func load_image(urlString:String)
{   let imgURL: NSURL = NSURL(string: urlString)!
    let request: NSURLRequest = NSURLRequest(URL: imgURL)

    NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()) { (response: NSURLResponse?, data: NSData?, error: NSError?) in

        if error == nil {
            self.image_element.image = UIImage(data: data)
        }
    }
}

การใช้งาน:

self.load_image(" url strig here")

sendAsynchronousRequest เลิกใช้แล้วใน iOS 9
Crashalot

5

Kingfisher เป็นหนึ่งในห้องสมุดที่ดีที่สุดสำหรับการโหลดภาพลงใน URL

URL Github - https://github.com/onevcat/Kingfisher

// If you want to use Activity Indicator.
imageview_pic.kf.indicatorType = .activity
imageview_pic.kf.setImage(with: URL(string: "Give your url string"))

// If you want to use custom placeholder image.
imageview_pic.kf.setImage(with: URL(string: "Give your url string"), placeholder: UIImage(named: "placeholder image name"), options: nil, progressBlock: nil, completionHandler: nil)

5

นี่คือรหัสการทำงานสำหรับการโหลด / ดาวน์โหลดภาพจาก URL NSCache โดยอัตโนมัติและแสดงภาพตัวยึดตำแหน่งก่อนดาวน์โหลดและโหลดภาพจริง (Swift 4 Code)

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
เฉพาะที่ทำงานกับฉัน ปัญหาของฉันคือฉันใช้งานเครื่องเล่นเพลงและฉันต้องการให้ภาพโหลดในการแจ้งเตือนเมื่อโทรศัพท์ปิดและผู้จัดการใช้รูปแบบของ UIImage ดังนั้นฉันจึงต้องใช้วิธีนี้
JhonnyTawk

4

วิธีการรับภาพที่ปลอดภัยและใช้งานได้กับ 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.
        }
    }

หากคุณกำลังอัปเดตมุมมองด้วยภาพคุณจะต้องใช้สิ่งนี้หลังจาก "ถ้าสำเร็จ {":

    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

หากคุณกำลังมองหาการใช้งานที่ง่ายมาก ๆ (สิ่งนี้ใช้ได้กับฉันใน 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)

ฉันนำมาใช้ภายใน tableview กับเซลล์ที่กำหนดเองที่มีเฉพาะภาพ

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

    }

คุณพูดถูกเพราะการดาวน์โหลดและการบีบอัดกำลังเกิดขึ้นในเธรดหลัก คุณควรทำ 2 ขั้นตอนเหล่านี้ในแบบอะซิงโครนัสในเธรดพื้นหลังอาจใช้คิวโกลบอล dispatch_async
Naishta

ใช่โดยการใช้เธรดพื้นหลังเราสามารถเพิ่มความเร็วในการดาวน์โหลดและหากจำเป็นเราสามารถเปลี่ยนตรรกะแทนสิ่งนี้เราสามารถใช้ sdWebImage หรือกรอบงานอื่น ๆ
Gourav Joshi

2

ฉันแนะนำให้ใช้ไลบรารี 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()
                }
            })

2

คุณสามารถใช้พ็อดSDWebImageเพื่อให้ได้เหมือนกัน มันใช้งานง่าย คุณสามารถรับเอกสารได้ที่นี่ SDWebImage

นี่คือตัวอย่างรหัส

self.yourImage.sd_setImage(with: NSURL(string: StrUrl as String ) as URL!, placeholderImage: placeholderImage, options: SDWebImageOptions(rawValue: 0), completed: { (image, error, cacheType, imageURL) in
                if( error != nil)
                {
                    print("Error while displaying image" , (error?.localizedDescription)! as String)
                }
            })

2

รวดเร็ว 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()
    imgView.load(url: "<imageURLHere>")
}

1

สิ่งเดียวที่ขาดหายไปคือ!

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

1

Swift 2.x ตอบว่าการดาวน์โหลดภาพไปยังไฟล์ (ตรงข้ามกับคำตอบของ Leo Dabus ซึ่งเก็บภาพไว้ในหน่วยความจำ) ตามคำตอบของ Leo Dabus และคำตอบของ Rob จากรับข้อมูลจาก NSURLSession DownloadTaskWithRequest จากตัวจัดการที่เสร็จสมบูรณ์ :

    // 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 ฉันได้ crated ฟังก์ชั่นเพียงแค่ส่ง 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()
      })
    }
  }
}

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

ฉันได้สร้างฟังก์ชั่นการเรียนที่เรียบง่ายในUtils.swiftชั้นเรียนของฉันสำหรับการเรียกวิธีการที่คุณสามารถเข้าถึงได้ง่ายclassname.methodnameและภาพของคุณจะถูกบันทึกใน NSCache โดยใช้ImageCaching.swiftคลาส

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 ไชโย :)


1

Swift 4.2 และ AlamofireImage

AlamofireImageหากมีการใช้ห้องสมุดไม่เป็นปัญหาคุณสามารถทำมันด้วยความช่วยเหลือของ ตัวอย่างของฉันมาจากGithub

ตัวอย่างรูปภาพตัวยึด:

let imageView = UIImageView(frame: frame)
let url = URL(string: "https://httpbin.org/image/png")!
let placeholderImage = UIImage(named: "placeholder")!
imageView.af_setImage(withURL: url, placeholderImage: placeholderImage)

มันมีฟังก์ชั่นที่มีประโยชน์มากมายและส่วนต่อขยายที่ใช้งานได้กับภาพ ตั้งแต่การแคชจนถึงการปรับขนาดและปรับขนาดหรือแม้แต่การใช้ฟิลเตอร์ในภาพ หากรูปภาพมีความสำคัญในแอปของคุณฉันแนะนำให้ใช้เฟรมเวิร์กนี้และประหยัดเวลาของคุณ


0

การใช้ Ascyimageview คุณสามารถโหลด imageurl ใน imageview ได้ง่าย

ให้ image1Url: URL = URL (สตริง: "(imageurl)" เป็นสตริง)! imageview.imageURL = image1Url


0

โหลดภาพจากเซิร์ฟเวอร์: -

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

0

เพื่อประสิทธิภาพที่ดีขึ้นUITableViewหรือUICollectionViewใช้ไลบรารี่ที่มีน้ำหนักเบาโหลดอย่างชาญฉลาด Lazyคุณสามารถใช้วิธีการโหลดแบบสันหลังยาวนี้หากคุณต้องการโหลดภาพจาก url Asynchronous

Smart 'Lazy Loading' ในUICollectionViewหรือUITableViewใช้NSOperationและNSOperationQueueใน iOS ดังนั้นในโครงการนี้เราสามารถดาวน์โหลดรูปภาพหลาย ๆ ภาพในมุมมองใด ๆ ( UICollectionViewหรือUITableView) โดยการปรับประสิทธิภาพของแอพให้เหมาะสมโดยใช้OperationและOperationQueueเพื่อการทำงานพร้อมกัน ต่อไปนี้เป็นจุดสำคัญของโครงการโหลดสมาร์ทขี้เกียจ: การสร้างบริการดาวน์โหลดภาพ จัดลำดับความสำคัญการดาวน์โหลดตามการมองเห็นของเซลล์

คลาส ImageDownloadService จะสร้างอินสแตนซ์เดี่ยวและมีอินสแตนซ์NSCacheเพื่อแคชรูปภาพที่ถูกดาวน์โหลด เราได้สืบทอดคลาสปฏิบัติการไปยัง TOperation เพื่อดึงการทำงานตามความต้องการของเรา ฉันคิดว่าคุณสมบัติของคลาสย่อยการดำเนินการค่อนข้างชัดเจนในแง่ของการใช้งาน เรากำลังตรวจสอบการเปลี่ยนแปลงการทำงานของสถานะโดยใช้ KVO

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.