I am making an iOS movie app using UIKIT and Swift and I wanted to display the movies in a collectionview but there's some kind of glitch and there are some kind of traingle being diplayed instead of the movie.
Could someone please help? Thanks
moviesViewController.swift
//
// FirstViewController.swift
// PopcornTime
//
// Created by Maxime Ruys on 15/03/2020.
// Copyright © 2020 Pixel-Developers. All rights reserved.
//
import UIKit
import SwiftyJSON
class MoviesViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
#IBOutlet weak var moviesCollectionView: UICollectionView!
var movies = [Movie]()
override func viewDidLoad() {
super.viewDidLoad()
self.moviesCollectionView.delegate = self
self.moviesCollectionView.dataSource = self
fetchMovies()
}
func fetchMovies(){
self.movies = []
let url = URL(string: "API_URL_HERE")
guard let requestUrl = url else { fatalError() }
var request = URLRequest(url: requestUrl)
request.httpMethod = "GET"
request.httpBody = "".data(using: String.Encoding.utf8)
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
if (error != nil) {
print(error)
} else {
if let data = data, let dataString = String(data: data, encoding: .utf8) {
do {
if let dataFromString = dataString.data(using: .utf8, allowLossyConversion: false) {
let json = try JSON(data: dataFromString)
for(_, movie) in json{
var imgs = [String]()
imgs.append(movie["images"]["banner"].stringValue)
imgs.append(movie["images"]["poster"].stringValue)
imgs.append(movie["images"]["fanart"].stringValue)
self.movies.append(Movie(id: movie["_id"].stringValue, title: movie["title"].stringValue, desc: movie["synopsis"].stringValue, playTime: movie["runtime"].stringValue, imgs: imgs))
}
}
} catch {
print(error)
}
DispatchQueue.main.async {
self.moviesCollectionView.reloadData()
}
}
}
}
task.resume()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.movies.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MoviesRowController",
for: indexPath) as! MoviesRowController
cell.moviesRowImg.image = downloadImage(from: URL(string: movies[indexPath.row].imgs[0])!)
cell.moviesTitleLbl.text = movies[indexPath.row].title
return cell
}
func downloadImage(from url: URL) -> UIImage{
getData(from: url) { data, response, error in
guard let data = data, error == nil else { return }
DispatchQueue.main.async() {
return UIImage(data: data)
}
}
return UIImage(named: "test")!
}
func getData(from url: URL, completion: #escaping (Data?, URLResponse?, Error?) -> ()) {
URLSession.shared.dataTask(with: url, completionHandler: completion).resume()
}
}
collectionview glitch image:
collectionview properties:
colletionview cell properties:
You can't assign a value that's asynchronous , so return here is nil
func downloadImage(from url: URL) -> UIImage{
getData(from: url) { data, response, error in
guard let data = data, error == nil else { return }
DispatchQueue.main.async() {
return UIImage(data: data)
}
}
return UIImage(named: "test")
}
use SDWebImage
You can do introspection like below.
if movies[indexPath.row].imgs.count>0, let url = URL(string: movies[indexPath.row].imgs[0]!) {
}
If you wish to Cache the images to avoid multiple requests, use below extension.
let imageCache = NSCache<NSString, UIImage>()
extension UIImageView {
func imageFromServerURL(_ 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: \(error)")
DispatchQueue.main.async {
self.image = placeHolder
}
return
}
DispatchQueue.main.async {
if let data = data {
if let downloadedImage = UIImage(data: data) {
imageCache.setObject(downloadedImage, forKey: NSString(string: URLString))
self.image = downloadedImage
}
}
}
}).resume()
}
}}
Your cell is not registered either:
self.collectionView.register(UICollectionViewCell.self, forCellReuseIdentifier: "movieCell")
Related
I'm trying to set an image on the button from API
here my code is
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! CreatGroupCollectionViewCell
cell.btn2.setImage(downloaded(link: String), for: .normal)
return cell
}
I also use an extension to download an image from the link
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() { [weak self] in
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)
}
}
please tell me how to set an image on the button
Try using this extension to set image from url in a UIButton:
extension UIButton {
func setImageFrom(url link: String) {
guard let url = URL(string: link) else { return }
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?.setImage(image, for: .normal)
}
}.resume()
}
}
Use this extension to load image from url and store it to cache for faster reload.
let imageCache = NSCache<AnyObject, AnyObject>()
extension UIImageView {
func loadImageUsingCacheWithUrlString(_ urlString: String) {
self.image = nil
//check cache for image first
if let cachedImage = imageCache.object(forKey: urlString as AnyObject) as? UIImage {
self.image = cachedImage
return
}
//otherwise fire off a new download
guard let url = URL(string: "\(urlString)") else { return }
URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in
//download hit an error so lets return out
if error != nil {
print(error ?? "")
return
}
DispatchQueue.main.async(execute: {
if let downloadedImage = UIImage(data: data!) {
imageCache.setObject(downloadedImage, forKey: urlString as AnyObject)
self.image = downloadedImage
}
})
}).resume()
}
}
I have a UITableviewCell in that cell I have added name, emp_id, and UIImageview to display the data I have 2 url's in which one url has displaying names and emp_id's and another Url have images along emp_id's(same emp_id's ) and I have to show that images to there names using help of emp_id's. I am able to show details but not able to implement the images here is my code
struct jsonstruct5:Decodable {
var name:String
var emp_id:String
var url:String?
}
struct jsonstruct21:Decodable {
var url:String?
var emp_id:String
}
var arrdata = [jsonstruct5]()
var arrdata1 = [jsonstruct21]()
func getdata(){
let url = URL(string: "https://sp/company/employees_detail/app")
URLSession.shared.dataTask(with: url!) { (data, response, error) in
do{if error == nil{
self.arrdata = try JSONDecoder().decode([jsonstruct5].self, from: data!)
for mainarr in self.arrdata{
// print(data)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
}catch{
print("Error in get json data")
}
}.resume()
}
Response is
[
{
"name":"Sonu",
"emp_id":"01"
},
{
"name":"Prasanth",
"emp_id":"02"
},
{
"name":"Patra",
"emp_id":"03"
}.
]
func getdata1(){
let url = URL(string: "https://sp/company/employees_detail/profile/photos")
URLSession.shared.dataTask(with: url!) { (data, response, error) in
do{if error == nil{
self.arrdata1 = try JSONDecoder().decode([jsonstruct21].self, from: data!)
for mainarr1 in self.arrdata1{
// print(mainarr.name,":",mainarr.dob)
print(data)
print(mainarr1.url)
let data1 = try? Data(contentsOf: url!)
print(data1)
if let imageData = data {
let image4 = UIImage(data: imageData)
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
}catch{
print("Error in get json data")
print(error)
}
}.resume()
}
Response is
[
{
"url":"https//ps/Image2",
"emp_id":"01"
},
{
"url":null,
"emp_id":"02"
},
{
"url":"https//ps/Image4",
"emp_id":"03"
}
]
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:AppreTableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell") as! AppreTableViewCell
cell.nameLbl.text = "\(arrdata[indexPath.row].name)"
cell.dateLbl.text = "\(arrdata[indexPath.row].emp_id)"
print(DataManager.sharedInstance.empID)
if (arrdata[indexPath.row].emp_id == DataManager.sharedInstance.empID)
{
cell.isHidden=true
}
else{
cell.isHidden=false
}
// tableView.alwaysBounceVertical = false
return cell
}
Try Below
struct jsonstruct5:Decodable {
var name:String
var emp_id:String
var url:String?
}
struct jsonstruct21:Decodable {
var url:String?
var emp_id:String
}
var arrdata = [jsonstruct5]()
var arrdata1 = [jsonstruct21]()
func getdata(){
let url = URL(string: "https://sp/company/employees_detail/app")
URLSession.shared.dataTask(with: url!) { (data, response, error) in
do { if error == nil {
self.arrdata = try JSONDecoder().decode([jsonstruct5].self, from: data!)
self. getdata1()
// don't for put reloadData in for loops, always play with your data/model and after loop you can call reloadData method.
}catch{
print("Error in get json data")
}
}.resume()
}
func getdata1(){
let url = URL(string: "https://sp/company/employees_detail/profile/photos")
URLSession.shared.dataTask(with: url!) { (data, response, error) in
do{if error == nil{
self.arrdata1 = try JSONDecoder().decode([jsonstruct21].self, from: data!)
// I don't know your model type, but you can put "url" from each iteration of second array (arrdata1) into first array (arrdata) where employee id matches.
// don't download images in below loop, rather download them in cellForRowAt method.
// then you just call self.tableView.reloadData()
// update your model
for item1 in arrdata1 {
if let index = arrdata.index(where: {$0["emp_id"] == item1["emp_id"]}) {
var item = arrdata[index]
item["url"] = item1["url"]
arrdata[index] = item
}
}
// or below code
for item1 in arrdata1 {
if let index = arrdata.index(where: {$0.emp_id == item1.emp_id}) {
var item = arrdata[index]
item.url = item1.url
arrdata[index] = item
}
}
// model array has been updated, now work on cellForRowAt method
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}catch{
print("Error in get json data")
print(error)
}
}.resume()
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:AppreTableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell") as! AppreTableViewCell
let model = arrdata[indexPath.row]
cell.nameLbl.text = "\(model.name)"
cell.dateLbl.text = "\(model.emp_id)"
// here you download image for only current model.url
if let url = model.url {
cell.nameOfImageView.loadImageUsingCacheWithURLString(url, placeHolder: UIImage(named: "someImage")!, completionBlock: { (image) in
cell.nameOfImageView.image = image
})
}
// tableView.alwaysBounceVertical = false
return cell
}
let imageCache = NSCache<NSString, UIImage>()
extension UIImageView {
func loadImageUsingCacheWithURLString(_ URLString: String, placeHolder: UIImage, completionBlock:#escaping (_ image:UIImage)->()) {
if let cachedImage = imageCache.object(forKey: NSString(string: URLString)) {
self.image = cachedImage
completionBlock(cachedImage)
}
else 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: \(error!.localizedDescription)")
DispatchQueue.main.async {
self.image = placeHolder
completionBlock(placeHolder)
}
}
else {
DispatchQueue.main.async {
if let data = data {
if let downloadedImage = UIImage(data: data) {
imageCache.setObject(downloadedImage, forKey: NSString(string: URLString))
self.image = downloadedImage
completionBlock(downloadedImage)
}
else {
self.image = placeHolder
completionBlock(placeHolder)
}
}
else {
self.image = placeHolder
completionBlock(placeHolder)
}
}
}
}).resume()
}
else if URLString.isEmpty {
completionBlock(placeHolder)
}
}
}
I don't know why but when I open the app, it takes a while to load all the data on the screen, until there remains a white screen without content. All the data loaded is downloaded from an API. What should I do to make it better?
App Loaded after about 10 seconds:
I'll post below how I'm parsing all the data.
ViewController.swift:
import UIKit
class ViewController: UIViewController, UICollectionViewDataSource, UITableViewDataSource {
#IBOutlet weak var tableViewTopSell: UITableView!
#IBOutlet var collectionView: UICollectionView!
#IBOutlet weak var collectionViewBanner: UICollectionView!
var dataSource: [Content] = [Content]()
var dataBanner: [Banner] = [Banner]()
var dataTopSold: [Top10] = [Top10]()
override func viewDidLoad() {
super.viewDidLoad()
//SetupNavBarCustom
self.navigationController?.navigationBar.CustomNavigationBar()
let logo = UIImage(named: "tag.png")
let imageView = UIImageView(image:logo)
self.navigationItem.titleView = imageView
//CallAPIData
getTopSold { (data) in
DispatchQueue.main.async {
self.dataTopSold = data
self.tableViewTopSell.reloadData()
}
}
getBanner { (data) in
DispatchQueue.main.async {
self.dataBanner = data
self.collectionViewBanner.reloadData()
}
}
getAudiobooksAPI { (data) in
DispatchQueue.main.async {
self.dataSource = data
self.collectionView.reloadData()
}
}
}
//CollectionView
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if (collectionView == self.collectionView) {
return self.dataSource.count
}else{
return self.dataBanner.count
}}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if (collectionView == self.collectionView) {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionViewCell", for: indexPath) as! CollectionViewCell
let content = self.dataSource[indexPath.item]
cell.bookLabel.text = content.descricao
cell.bookImage.setImage(url: content.urlImagem, placeholder: "")
return cell
}else if (collectionView == self.collectionViewBanner) {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionViewCellBanner", for: indexPath) as! CollectionViewCell
let content = self.dataBanner[indexPath.item]
cell.bannerImage.setImage(url: content.urlImagem, placeholder: "")
return cell
}
return UICollectionViewCell()
}
//TableView
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.dataTopSold.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "topSoldCell", for: indexPath) as! TableViewCell
let content = self.dataTopSold[indexPath.item]
cell.labelNomeTopSell.text = content.nome
cell.imageViewTopSell.setImage(url: content.urlImagem, placeholder: "")
return cell
}
}
extension UIImageView{
func setImage(url : String, placeholder: String, callback : (() -> Void)? = nil){
self.image = UIImage(named: "no-photo")
URLSession.shared.dataTask(with: NSURL(string: url)! as URL, completionHandler: { (data, response, error) -> Void in
guard error == nil else{
return
}
DispatchQueue.main.async(execute: { () -> Void in
let image = UIImage(data: data!)
self.image = image
if let callback = callback{
callback()
}
})
}).resume()
}
}
DataStore.swift:
import Foundation
import UIKit
func getBanner(_ completion:#escaping ([Banner])->Void) {
let url = URL(string: "https://alodjinha.herokuapp.com/banner")
let session = URLSession.shared
guard let unwrappedURL = url else { print("Error unwrapping URL"); return }
let dataTask = session.dataTask(with: unwrappedURL) { (data, response, error) in
guard let unwrappedDAta = data else { print("Error unwrapping data"); return }
do {
let jsonBanner = try JSONDecoder().decode(BannerData.self, from: unwrappedDAta)
completion(jsonBanner.data)
} catch {
print("Could not get API data. \(error), \(error.localizedDescription)")
}
}
dataTask.resume()
}
func getTopSold(_completion:#escaping ([Top10])->Void) {
let url = URL(string: "https://alodjinha.herokuapp.com/produto/maisvendidos")
let session = URLSession.shared
guard let unwrappedURL = url else { print("Error url"); return}
let dataTask = session.dataTask(with: unwrappedURL) { (data, response, error) in
guard let unwrappedData = data else { print("Error data"); return}
do {
let jsonTop10 = try JSONDecoder().decode(Top10Data.self, from: unwrappedData)
_completion(jsonTop10.data)
}catch{
print("Could no get API data")
}
}
dataTask.resume()
}
Model.swift:
import Foundation
//Categorias
struct Contents : Decodable {
let data : [Content]
}
struct Content : Decodable {
let id : Int
let descricao : String
let urlImagem : String
}
//Banner
struct BannerData : Decodable {
let data : [Banner]
}
struct Banner : Decodable {
let id : Int
let urlImagem : String
let linkUrl : String
}
//Top10
struct Top10Data:Decodable {
let data: [Top10]
}
struct Top10:Decodable {
let id : Int
let nome : String
let urlImagem : String
}
Apart from it's a heavy network loading VC , you Currently don't have any problems loading the data as all are out of main thread , the only problem is loading the images as when you scroll it re-download the image again which may be just downloaded for this i recommend using SDWebImage which will take care of the download & cache for you , your main problem may be low network speed for many requests
Another thing to manage this network problem you may serial queue the download of the data that will help you load one part fastly and display it which will make the impression to the user that the app is in request for more data instead of make all the requests at once
I run this code as an extension to imageView to load image to cells.the problem is images are loading to cells but all cells image are invisible or hidden till I click on one of them after that all cells images are shown !
extension UIImageView {
func downloadImageFrom(link:String, contentMode: UIViewContentMode) {
URLSession.shared.dataTask( with: NSURL(string:link)! as URL, completionHandler: {
(data, response, error) -> Void in
DispatchQueue.main.async {
self.contentMode = contentMode
if let data = data { self.image = UIImage(data: data) }
}
}).resume()
}
}
and in the cellforRowAt :
cell.imageView?.downloadImageFrom (link)
There is less info posted on question still I can assume following may be issue
-> Your cell has UIImageView and you are downloading image but after image download how you are notifying cell ? ,
-> Since tableview reuse cells and your imageview will also been re used by tableview cell in your code you are just downloading image data and set to UIImageView that will create problem in future
TIP:
1) Use disk or memory caching ,
2) Use placeholder image while your cell is downloading image
3) Use Completion handler (closure) to notify your cell when you have finished downloading
4) You can use third party library which is very ease to use like AlamofireImage or SDWebImage
You can use following code : -
extension UIImageView {
static let cacheData = NSCache<AnyObject, AnyObject>()
func downloadedFrom(link: String, placeHolder:String = "placeholder",isFromCache:Bool = true,isIndicator:Bool = true,isAppendBaseUrl:Bool = true) {
let placeHolderImage = UIImage.init(named: placeHolder);
self.contentMode = UIViewContentMode.scaleAspectFill
self.clipsToBounds = true;
self.image=placeHolderImage;
var strlink = ""
if isAppendBaseUrl
{
strlink = "Your base url" + link
}
else
{
strlink = link
}
let activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.whiteLarge)
guard let url = URL(string: strlink) else
{
return
}
if isIndicator
{
activityIndicator.center = CGPoint(x: self.bounds.size.width/2, y: self.bounds.size.height/2)
activityIndicator.color = UIColor.white
self.addSubview(activityIndicator)
activityIndicator.startAnimating()
}
if isFromCache
{
if let cachedImage = UIImageView.cacheData.object(forKey: url as AnyObject) as? UIImage
{
if isIndicator
{
activityIndicator.stopAnimating()
}
self.image = cachedImage
}
else
{
self.image = placeHolderImage
let urlStr = strlink
let url = URL(string: urlStr)
let request: URLRequest = URLRequest(url: url!)
URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) in
if error != nil
{
print(error!)
return
}
DispatchQueue.main.async(execute: { () -> Void in
let image = UIImage(data: data!)
if isIndicator
{
activityIndicator.stopAnimating()
activityIndicator.removeFromSuperview();
}
if (image != nil)
{
self.image = image
UIImageView.cacheData.setObject(image!, forKey: url as AnyObject)
}
})
}).resume()
}
}
else
{
self.image=placeHolderImage;
let urlStr = strlink
let url = URL(string: urlStr)
var request: URLRequest = URLRequest(url: url!)
request.setValue("xyz", forHTTPHeaderField:"DeviceId")
URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) in
if error != nil
{
print(error!)
return
}
DispatchQueue.main.async(execute: { () -> Void in
let image = UIImage(data: data!)
if isIndicator
{
activityIndicator.stopAnimating()
activityIndicator.removeFromSuperview();
}
if (image != nil)
{
self.image = image
UIImageView.cacheData.setObject(image!, forKey: url as AnyObject)
}
})
}).resume()}
}}
I solved my problem by adding a subview in table cells and hold images
Try this, it works for me. May be forgot to specify a method super.viewDidLoad() or super.viewDidAppear(true)...
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "idFriendCell", for: indexPath) as! FriendCell
cell.fotoFriend.downloadImageFrom(link: "http://foto.jpg")
return cell
}
extension UIImageView {
func downloadImageFrom(link: String) {
URLSession.shared.dataTask(with: URL(string: link)!) { (data, response, error) in
DispatchQueue.main.async {
guard let data = data else { return }
self.image = UIImage(data: data)
}
}.resume()
}
}
So I am making a network request. I parse the JSON to custom Objects. In these objects there are urls which are suppose to bring back images. One of the URL returns an error message (404) so there ins't anything there! How can I set a default image in its place and stop my app from crashing? Here is my code! Thanks
import UIKit
class HomepageCollectionViewController: UICollectionViewController {
var imageCache = NSCache()
var hingeImagesArray = [HingeImage]()
var arrayToHoldConvertedUrlToUIImages = [UIImage]()
var task: NSURLSessionDataTask?
override func viewDidLoad() {
super.viewDidLoad()
// Makes the network call for HingeImages
refreshItems()
}
override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return hingeImagesArray.count
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("imageReuseCell", forIndexPath: indexPath) as! ImageCollectionViewCell
let image = hingeImagesArray[indexPath.row]
if let imageURL = image.imageUrl {
if let url = NSURL(string: imageURL) {
//settingImageTpChache
if let myImage = imageCache.objectForKey(image.imageUrl!) as? UIImage {
cell.collectionViewImage.image = myImage
}else {
// Request images asynchronously so the collection view does not slow down/lag
self.task = NSURLSession.sharedSession().dataTaskWithURL(url, completionHandler: { (data, response, error) -> Void in
// Check if there is data returned
guard let data = data else {
return
}
// Create an image object from our data and assign it to cell
if let hingeImage = UIImage(data: data){
//cachedImage
self.imageCache.setObject(hingeImage, forKey: image.imageUrl!)
dispatch_async(dispatch_get_main_queue(), { () -> Void in
cell.collectionViewImage.image = hingeImage
//append converted Images to array so we can send them over to next view - only proble in that the only images converted at the ones you scrool to which is retarted
self.arrayToHoldConvertedUrlToUIImages.append(hingeImage)
print(self.arrayToHoldConvertedUrlToUIImages)
})
}
})
task?.resume()
}
}
}
return cell
}
you can check if error is not nil then set deafult image .
self.task = NSURLSession.sharedSession().dataTaskWithURL(url, completionHandler: { (data, response, error) -> Void in
if error != nil {
cell.collectionViewImage.image = UIImage(named:"default_image")
return
}
...
Try this:
let imageCache = NSCache<AnyObject, AnyObject>()
extension UIImageView {
func loadImageUsingCacheWithUrl(urlString: String) {
self.image = nil
// check for cache
if let cachedImage = imageCache.object(forKey: urlString as AnyObject) as? UIImage {
self.image = cachedImage
return
}
// download image from url
let url = URL(string: urlString)
URLSession.shared.dataTask(with: url!, completionHandler: { (data, response, error) -> Void in
var image:UIImage
if error == nil {
if(UIImage(data: data!) != nil){
image = UIImage(data: data!)!
} else {
image = UIImage(named: "DefaultImage")!
}
} else {
print(error ?? "load image error")
return
}
DispatchQueue.main.async(execute: { () -> Void in
imageCache.setObject(image, forKey: urlString as AnyObject)
self.image = image
})
}).resume()
}
}
The key point is with 404 return message, data task error is still = nil and this time you must check UIImage(data: data!) != nil to prevent a “fatal error: unexpectedly found nil while unwrapping an Optional value”