I am new to iOS i want download image to display it is working code but here lot of code duplication
let url = URL(string: iteminfo.imageUrl!)
let urlRequest = URLRequest(url: url!)
let task = URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
if error != nil {
print(error)
}
if let data = data {
print(data)
self.imageViewItemPic.image = UIImage(data: data)
}
}
task.resume()
let url2 = URL(string: iteminfo.cookerProfilePicUrl!)
let urlRequest2 = URLRequest(url: url2!)
let task2 = URLSession.shared.dataTask(with: urlRequest2) { (data, response, error) in
if error != nil {
print(error)
}
if let data = data {
print(data)
self.imageViewCookerProfilePic.image = UIImage(data: data)
}
}
task2.resume()
So I want to reuse my code but i unfortunately i can not reach my goal. there have no error and url is correct . every time goes else statement . i am missing something but what is that ?
if let image = downlaodImage(urlImage: iteminfo.imageUrl){
print("first \(image)")
imageViewItemPic.image = image
}else{
print("first wrong......")
}
if let image = downlaodImage(urlImage: iteminfo.cookerProfilePicUrl){
print("second \(image)")
imageViewCookerProfilePic.image = image
}
else{
print("second wrong......")
}
Here is my method :
func downlaodImage(urlImage : String?) -> UIImage?{
var image : UIImage?
let url = URL(string: urlImage!)
let urlRequest = URLRequest(url: url!)
let task = URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
if let data = data {
// print(data)
image = UIImage(data: data)
}
}
task.resume()
return image
}
note: I am not sure is it best way or not . if it is not best practice feel free to guide me .
There's no need of so much hassle. You have the URL of the image so you can simply download the image from the URL. For example:
func downloadImage(imageURL: String) {
DispatchQueue.global().async {
let data = NSData.init(contentsOf: NSURL.init(string: imageURL) as! URL)
DispatchQueue.main.async {
let image = UIImage.init(data: data as! Data)
imageView.image = image
}
}
}
Edit: To reuse this code I would suggest to use extension of UIImageView. Here's an example:
extension UIImageView {
func setImageFromURL(url: String) {
DispatchQueue.global().async {
let data = NSData.init(contentsOf: NSURL.init(string: url) as! URL)
DispatchQueue.main.async {
let image = UIImage.init(data: data as! Data)
self.image = image
}
}
}
}
Use this method whenever you want to set the image of an imageView from a url like this:
self.imageViewCookerProfilePic.setImageFromURL(url: "https://encrypted-tbn1.gstatic.com/images?q=tbn:ANd9GcQNpKmjx1w3DRDZ9IXN81-uhSUA6qL6obkOthoUkb9RZkXf5pJ8")
Dude. You should learn some staff about async and sync code.
Here is the thing. Code in you downloadImage works synchronically, so it pass you URLTask and go straight to return, there you return you image variable, that is nil.
One of the solutions in to use callback block like this:
func downloadImage(urlImage : String?, complete: ((UIImage?)->Void)? = nil){
let url = URL(string: urlImage!)
let urlRequest = URLRequest(url: url!)
let task = URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
if let data = data {
complete?(UIImage(data: data))
}
}
task.resume()
}
And then use it like this:
{ ...
downloadImage(urlImage: "", complete: { image in
if let image = image{
self.imageViewItemPic.image = image
}else{
print("no image")
}
})
...
}
You should read some tutorials about async code and web in swift. You could start with this site
downlaodImage() downloads an image asynchronously so
if let image = downlaodImage(...) { ... }
is always going to fail because program execution has continued before your response data has come back.
It would be easier just to set your images in the callback function closure of downlaodImage() as below by adding a UIImageView parameter to downlaodImage(). This way you can reduce the repetition of if else blocks by moving them to the downlaodImage function.
func downlaodImage(urlImage : String?, imageView: UIImageView) -> UIImage?{
var image : UIImage?
let url = URL(string: urlImage!)
let urlRequest = URLRequest(url: url!)
let task = URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
if let data = data {
// print(data)
if let image = UIImage(data: data) {
imageView.image = image
} else {
print("failed to load image")
}
}
}
task.resume()
return image
}
Simplified code without if/else blocks
downlaodImage(urlImage: "https://encrypted-tbn1.gstatic.com/images?q=tbn:ANd9GcQNpKmjx1w3DRDZ9IXN81-uhSUA6qL6obkOthoUkb9RZkXf5pJ8", imageView: imageViewItemPic)
downlaodImage(urlImage: "https://www.dominos.co.nz/ManagedAssets/OLO/eStore/all/Product/NZ/P015/P015_ProductImage_Small_en_Default_20140203_105245.png", imageView: imageViewCookerProfilePic)
Related
This question already has answers here:
Loading/Downloading image from URL on Swift
(39 answers)
Closed 3 years ago.
I am trying to load image from url in my ios app swift. I have written following code.
let imageURL = minHost + "\(userData["profileImage"])"
let url = URL(string: imageURL)!
let imageData = try? Data(contentsOf: url)
profileImage.image = UIImage(data: imageData!)
Now imageURL is having proper url, but imageData receives nil and because of this, last line through an error Fatal error: Unexpectedly found nil while unwrapping an Optional value
Instead of fetching image using Data(contentsOf:) method, use URLSession to perform network calls.
let imageURL = minHost + "\(userData["profileImage"])"
if let url = URL(string: imageURL) {
URLSession.shared.dataTask(with: url) {[weak self] (data, urlResponse, error) in
if let data = data {
DispatchQueue.main.async {
self?.profileImage.image = UIImage(data: imageData)
}
}
}.resume()
}
Important Note: Avoid using forced unwrapping (!) unnecessarily. It might result in unwanted app crashes. Instead use guard or if-let to unwrap optionals.
Try this at Playground.
Loading image from the URL takes some time, and need to be executed at another Thread, different from the main thread.
import UIKit
let url = URL(string: "https://cdn.arstechnica.net/wp-content/uploads/2018/06/macOS-Mojave-Dynamic-Wallpaper-transition.jpg")!
var image = UIImage()
DispatchQueue.global().async {
if let data = try? Data(contentsOf: url) {
DispatchQueue.main.async {
image = UIImage(data: data)!
}
}
}
image
you can try like this:
let url = URL(string: "image url here")
if url != nil {
DispatchQueue.global().async { [weak self] in
if let data = try? Data(contentsOf: url!) {
if let image = UIImage(data: data) {
DispatchQueue.main.async {
self.profileImage.image = image
}
}
}
}
}
Try This
let url = URL(string:imageURL)
if let data = try? Data(contentsOf: url!)
{
profileImage.image = UIImage(data: data, scale: 1.0)!
}
Never do the downloading task on main thread. if you do, you will not able to access components in current visible screens properly. It should be always on the background thread.
if let url = URL(string: "https://....") {
DispatchQueue.global(qos: .background).async {
if let data = try? Data(contentsOf: url) {
if let image = UIImage(data: data) {
DispatchQueue.main.async {
self.profileImage.image = image
}
}
}
}
}
I have an array filled with parsed json data including image url's. But when i try to see that images inside uiimageview, it doesn't the show. What should i do
I printed the url. This is my url inside array.
This is my array
var feedResult = [Result]()
It shows the name inside collectionview but i couldn't see the images. I used named like everybody does. But what is missing?
let info = feedResult[indexPath.row]
cell.appLabel.text = info.artistName
cell.customCollectionImage.image = UIImage(named: info.artWorkUrl)
You have to download the image Data using the url you got, only then you will use the downloaded data, like so:
imageView.image = UIImage(data: downloadedData)
Here is a quick subclass of UIImageView that does the downloading:
class URLImageView: UIImageView {
func download(url urlString: String) {
guard let url = URL(string: urlString) else { return }
let task = URLSession.shared.dataTask(with: url) { (downloadedData, _, error) in
guard error == nil && downloadedData != nil else { return }
DispatchQueue.main.async{
self.image = UIImage(data: downloadedData!)
}
}
task.resume()
}
}
Update-1 Use the download function using UIImageView extension, without subclassing, like so:
extension UIImageView {
func download(url urlString: String) {
guard let url = URL(string: urlString) else { return }
let task = URLSession.shared.dataTask(with: url) { (downloadedData, _, error) in
guard error == nil && downloadedData != nil else { return }
DispatchQueue.main.async{
self.image = UIImage(data: downloadedData!)
}
}
task.resume()
}
}
Usage:
cell.customCollectionImage.download(url: info.artWorkUrl)
By using UIImage(named: info.artWorkUrl) you are not accessing the image in your array but the images in your Assets.xcassets (assets that you add manually in your project).
You need to download the image from the artWorkUrl and then directly use the downloaded image like this:
cell.customCollectionImage.image = UIImage(data: yourImageData)
Where yourImageData is what you have downloaded from the server with the artWorkUrl.
In my first swift project I face some problems by adding a new UIImage to the SubView after downloading the content of this image.
It looks like the download is done pretty fast but the App needs another 5-15 seconds to update the view. I have no clue why.
Here is what I have done:
override func viewDidLoad() {
super.viewDidLoad()
//...
loadPic(PrevPic)
}
func loadPic(prevImage: UIImageView){
//... get URL; result in object["data"]
let fileUrl = NSURL(string: object["data"] as! String)
let session = NSURLSession.sharedSession()
let request = NSMutableURLRequest(URL: fileUrl!)
let task = session.dataTaskWithRequest(request){
(data, response, error) -> Void in
if error != nil {
print(error!.localizedDescription)
} else {
var imagePic: UIImageView?
let imageData = NSData(data: data!)
prevImage.removeFromSuperview()
let image: UIImage = UIImage(data: imageData)!
imagePic = UIImageView(image: image)
imagePic?.contentMode = .ScaleAspectFit
self.view.addSubview(imagePic!)
//... alignment
}
}
task.resume()
//...
}
An ideas why?
Thanks for the support.
You dont have to remove UIImageView from your view and add it back. That is taking a lot of time. You should replace imageView's old image with new one.You can use following code.
if let newImage = UIImage(data: imageData){
imageView.image = newImage
}
When call for the web service then the task execute on main thread thats why the UI become freeze and none responsive. To get rid of this problem call the main queue and the update the UI like this:
func loadPic(prevImage: UIImageView){
//... get URL; result in object["data"]
let fileUrl = NSURL(string: object["data"] as! String)
let session = NSURLSession.sharedSession()
let request = NSMutableURLRequest(URL: fileUrl!)
let task = session.dataTaskWithRequest(request){
(data, response, error) -> Void in
if error != nil {
print(error!.localizedDescription)
} else {
var imagePic: UIImageView?
let imageData = NSData(data: data!)
dispatch_async(dispatch_get_main_queue(), {
prevImage.removeFromSuperview()
let image: UIImage = UIImage(data: imageData)!
imagePic = UIImageView(image: image)
imagePic?.contentMode = .ScaleAspectFit
self.view.addSubview(imagePic!)
//... alignment
})
}
}
task.resume()
//...
}
The basic idea is
dispatch_async(dispatch_get_main_queue(), {
// update UI
})
i am getting images from url's and displaying it in tableview which happens successfully but when i scroll it automatically and repeatedly changes but after i pause at that point for a couple of seconds (roughly 10 or more which is a pretty long time) the proper image loads.
func load_image(urlString:String)
{
let imgURL: NSURL = NSURL(string: urlString)!
let request: NSURLRequest = NSURLRequest(URL: imgURL)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request){
(data, response, error) -> Void in
if (error == nil && data != nil)
{
func display_image()
{
cell.pic.image = UIImage(data: data!)
}
dispatch_async(dispatch_get_main_queue(), display_image)
}
}
task.resume()
}
load_image(urls[indexPath.row])
First of all, why are You loading images in, I guess, cellForRowAtIndexPath?
You can load it in viewDidLoad and store in some array. But if You need to do this as You are doing...
For better performance You can use NSCache to prevent loading images everytime tableview will show your cell. Try something like that:
let imageCache = NSCache()
func load_image(urlString:String)
{
if let imageFromCache = imageCache.objectForKey(urlString) as? UIImage {
cell.pic.image = imageFromCache
return
}
let imgURL: NSURL = NSURL(string: urlString)!
let request: NSURLRequest = NSURLRequest(URL: imgURL)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request){
(data, response, error) -> Void in
if (error == nil && data != nil)
{
func display_image()
{
let imageToCache = UIImage(data: data!)
cell.pic.image = UIImage(data: data!)
imageCache.setObject(imageToCache!, forKey: urlString)
}
dispatch_async(dispatch_get_main_queue(), display_image)
}
}
task.resume()
}
For more informations about NSCache you can check documentation.
Your cell is being reused when scrolling. The cellForRowAt method will be called on scrolling.
Set imageView.image = nil in cellForRowAt method so no image will be display on the image view until they get downloaded.
We make imageView.image = nil is because if no image available for that cell then no image should be shown for that cell.
I want to Load image using JSON parsing in collectionView. I'm getting array of image URLs, but images are not shown on UIImageView. I have used this code after getting JSON array and displaying it in UICollectionView's UIImageView.
if let url = NSURL(string: self.Products[indexPath.row].image) {
if let data = NSData(contentsOfURL: url){
cell.product_image.contentMode = UIViewContentMode.ScaleAspectFit
cell.product_image.image = UIImage(data: data)
}
}
But I am not able to load image, but text is displayed. I have used this code in cellForItemAtIndexPath method.. Can anyone suggest me what am I doing wrong?
func jsonParsingFromURL(){
// 1
let reposURL = NSURL(string: "http://api.room2shop.com/api/product/GetProducts?categoryId=24&filter=2&pageNumber=1")
// 2
if let JSONData = NSData(contentsOfURL: reposURL!) {
// 3
do
{
if let json = try NSJSONSerialization.JSONObjectWithData(JSONData, options: .AllowFragments) as? NSDictionary {
// 4
if let reposArray = json["ProductList"] as? [NSDictionary] {
// 5
for item in reposArray {
Products.append(ProductList(json: item))
}
}
}
}
catch {
print("Error with Json: \(error)")
}
}
}
This is my JSON parsing code
to reflect changes, you need to use
self.collectionView?.reloadData()
EDIT
1- please replace this block with this, and tell me if you get the urls normally, for me I get the urls normally
for item in reposArray
{
//ProductList(json: item)
//Products.append(ProductList(json: item))
//print(item)
print("__________")
print(item["Image"]!)
}
2- i was getting
Transport Security has Blocked a cleartext HTTP
solution was here.
Transport security has blocked a cleartext HTTP
use this
// taking the URL , then request image data, then assigne UIImage(data: responseData)
let imgURL: NSURL = NSURL(string: "www.example.com/image.jpg")!
let request: NSURLRequest = NSURLRequest(URL: imgURL)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request) {
(data,response,error) -> Void in
if ( error == nil && data != nil ) {
func display_image() {
// imageView.post.image = your UIImage
self.imageViewPost.image = UIImage(data: data!)
}
dispatch_async(dispatch_get_main_queue(), display_image)
}
}
task.resume()
// end of loading img
The image is a URL which is not saved locally inside the image.xassets, so you need to parse the URL using the extension given below.
// add the extension
extension UIImageView {
func loadImage(urlString: String) {
let url = URL(string: urlString)!
URLSession.shared.dataTask(with: url, completionHandler: { (data, respones, error) in
if error != nil {
print(error ?? "")
return
}
DispatchQueue.main.async {
self.image = UIImage(data: data!)
}
}).resume()
}
}
// call this statement in your vc (where you polpulate)
yourImageView.loadImage(urlString:yourUrl)