Загрузка / загрузка изображения из URL на Swift
Я хотел бы загрузить изображение из 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, и как я могу решить эту проблему.
28 ответов:
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() { 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") if let url = URL(string: "http://www.apple.com/euro/ios/ios8/a/generic/images/og.png") { imageView.contentMode = .scaleAspectFit 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) { 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() { self.image = image } }.resume() } func downloaded(from link: String, contentMode mode: UIViewContentMode = .scaleAspectFit) { guard let url = URL(string: link) else { return } downloaded(from: url, contentMode: mode) } }использование:
imageView.downloaded(from: "http://www.apple.com/euro/ios/ios8/a/generic/images/og.png")
(Swift 4 update) Чтобы ответить на исходный вопрос напрямую, вот быстрый эквивалент опубликованного фрагмента 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-адреса одновременно в том же потоке выполняется код, так что не ссылаться на это в главном потоке приложения.легкий способ чтобы один и тот же код выполнялся асинхронно, не блокируя пользовательский интерфейс, используется GCD:
let url = URL(string: image.url) DispatchQueue.global().async { let data = try? Data(contentsOf: url!) //make sure your image in this url does exist, otherwise unwrap in a if let check / try-catch DispatchQueue.main.async { imageView.image = UIImage(data: data!) } }тем не менее, в реальных приложениях, если вы хотите иметь лучший пользовательский опыт и избежать нескольких загрузок одного и того же изображения, вы можете также иметь их не только загруженными, но и кэшированными. Есть уже довольно много библиотек, которые делают это очень бесшовно, и все они очень просты в использовании. Я лично рекомендую Зимородок:
import Kingfisher let url = URL(string: "url_of_your_image") // this downloads the image asynchronously if it's not cached yet imageView.kf.setImage(with: url)и вот это
Если вы просто хотите загрузить изображение (асинхронно!) - просто добавьте это небольшое расширение в свой swift-код:
extension UIImageView { public func imageFromUrl(urlString: String) { if let url = NSURL(string: urlString) { let request = NSURLRequest(URL: url) NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()) { (response: NSURLResponse?, data: NSData?, error: NSError?) -> Void in if let imageData = data as NSData? { self.image = UIImage(data: imageData) } } } } }и использовать его таким образом:
myImageView.imageFromUrl("https://robohash.org/123.png")
Swift 2.2|/Xcode 7.3
Я получил удивительные результаты!! с AlamofireImage swift библиотека
Он предоставляет несколько функций, таких как:
- асинхронно скачать
- автоматическая очистка Кэша изображений, Если предупреждения памяти происходят для приложения
- URL изображения кэширование
- Кэширование Изображения
- Избегайте Повторяющихся Загрузок
и очень легко реализовать для своего приложения
шаг.1 Установите модули
Alamofire 3.3.x
pod 'Alamofire'
AlamofireImage 2.4.x
pod 'AlamofireImage'
шаг.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, ребята!--3-->, для облегчения жизни идевелоперов;)
Xcode 8* Swift 3
ответ Лео Дабуса является удивительным! Я просто хотел обеспечить решение функции "все-в-одном":
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()
я завернул код лучших ответов на вопрос в один многоразовый класс, расширяющий 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")
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!) } } } }
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)
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 с обработкой ошибок
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 { 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")
ты хочешь:
UIImage(data: data)в Swift они заменили большинство методов Objective C factory обычными конструкторами.
посмотреть:
Swift 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 }
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 }
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: ) } .flatMap { UIImage(data: ) }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")
Kingfisher-одна из лучших библиотек для загрузки изображений в URL.
GitHub URL -https://github.com/onevcat/Kingfisher
// If you want to use Activity Indicator. imageview_pic.kf.indicatorType = .activity imageview_pic.kf.setImage(with: URL(string: "Give your url string")) // If you want to use custom placeholder image. imageview_pic.kf.setImage(with: URL(string: "Give your url string"), placeholder: UIImage(named: "placeholder image name"), options: nil, progressBlock: nil, completionHandler: nil)
метод получения изображения, который является безопасным и работает с 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 }причина, по которой эта последняя часть необходима, если вы используете изображение в пользовательском интерфейсе, заключается в том, что сетевые вызовы занимают время. При попытке обновить пользовательский интерфейс с помощью образа без вызова dispatch_async как и выше, компьютер будет искать изображение, пока изображение все еще извлекается, найти, что нет изображения (пока), и двигаться дальше, как если бы не было найдено изображение. Помещая свой код внутри закрытия завершения dispatch_async говорит компьютеру: "идите, получите этот образ, и когда вы закончите, затем завершите этот код."Таким образом, у вас будет изображение, когда код будет вызван, и все будет работать хорошо.
Если вы ищете очень простую реализацию. (Это работает для меня в 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 }
единственное, чего там не хватает-это 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!)
Swift 2.X ответ, который загружает изображение в файл (в отличие от ответа Лео Дабуса, который хранит изображение в памяти). На основе ответа Лео Дабуса и ответа Роба от получить данные из 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 }
вот рабочий код для загрузки / загрузки изображения из URL. Nscache автоматически и отображает изображение заполнителя перед загрузкой и загрузкой фактического изображения (код 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! } } } } } } }используйте вот так:
// 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 }
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) } } })Счастливого Кодирования. Ура:)
Swift 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)
Я рекомендую использовать библиотеку Kingfisher для асинхронной загрузки изображений. Лучшая часть использования Kingfisher-это кэширование всех загруженных изображений по умолчанию с url-адресом изображения в качестве идентификатора. В следующий раз, когда вы запросите загрузить изображение с этим конкретным 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() } })
используйте этот код в Swift
imageView.image=UIImage(data: NSData(contentsOfURL: NSURL(string: "http://myURL/ios8.png")!)!
использование Ascyimageview вы можете легко загрузить url_изображения в графическое представление.
пусть image1Url: URL = URL (строка:" (imageurl) " как строка)! просмотрщик фото.imageURL = image1Url
Swift 4.1 я создал функцию просто передать url-адрес изображения, ключ кэша после создания изображения установите его в блок завершения.
class NetworkManager: NSObject { private var imageQueue = OperationQueue() private var imageCache = NSCache<AnyObject, AnyObject>() func downloadImageWithUrl(imageUrl: String, cacheKey: String, completionBlock: @escaping (_ image: UIImage?)-> Void) { let downloadedImage = imageCache.object(forKey: cacheKey as AnyObject) if let _ = downloadedImage as? UIImage { completionBlock(downloadedImage as? UIImage) } else { let blockOperation = BlockOperation() blockOperation.addExecutionBlock({ let url = URL(string: imageUrl) do { let data = try Data(contentsOf: url!) let newImage = UIImage(data: data) if newImage != nil { self.imageCache.setObject(newImage!, forKey: cacheKey as AnyObject) self.runOnMainThread { completionBlock(newImage) } } else { completionBlock(nil) } } catch { completionBlock(nil) } }) self.imageQueue.addOperation(blockOperation) blockOperation.completionBlock = { print("Image downloaded \(cacheKey)") } } } } extension NetworkManager { fileprivate func runOnMainThread(block:@escaping ()->Void) { if Thread.isMainThread { block() } else { let mainQueue = OperationQueue.main mainQueue.addOperation({ block() }) } } }
вы можете использовать pod
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) } })
для Swift-3 и выше:
extension UIImageView { public func imageFromUrl(urlString: String) { if let url = URL(string: urlString) { let request = URLRequest(url: url) NSURLConnection.sendAsynchronousRequest(request as URLRequest, queue: .main, completionHandler: { (response, data, error) in if let imageData = data as NSData? { self.image = UIImage(data: imageData as Data) } }) } } }
Comments