I have a problem trying to load an image from a controller to another. The problem is that when trying to load the new Controller runs 2 times the same class.
Code in ViewController
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
let row = ids[indexPath.row]
let next = self.storyboard?.instantiateViewControllerWithIdentifier("NewsController") as! NewsController
next.id = row
self.presentViewController(next, animated: true, completion: nil)
}
And code in NewsController:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// Retrieve cell
let cellIdentifier: String = "NewCell"
let myCell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as!NewDetailCell
// Get the location to be shown
let item: LocationModel = feedItems[indexPath.row] as! LocationModel
myCell.titLabel!.text = item.titulo
myCell.subLabel!.text = item.subtitulo
myCell.cuerpoLabel!.text = item.cuerpo
var urlimg : String = item.imgNot!
if urlimg == ""{
urlimg = "abc.jpg"
}
var image = UIImageView()
image = loadImageFromUrl("MyURL" + urlimg, view: myCell.newImage!)!
myCell.newImage = image
.
.
.
loadImageFromUrl():
func loadImageFromUrl(url: String!, view: UIImageView!) -> UIImageView?{
// Create Url from string
let url = NSURL(string: url)!
// Download task:
// - sharedSession = global NSURLCache, NSHTTPCookieStorage and NSURLCredentialStorage objects.
let task = NSURLSession.sharedSession().dataTaskWithURL(url) { (responseData, responseUrl, error) -> Void in
// if responseData is not null...
if let data = responseData{
// execute in UI thread
dispatch_async(dispatch_get_main_queue(), { () -> Void in
view.image = UIImage(data: data)
})
}
}
// Run task
task.resume()
return UIImageView (image : view.image)
}
THANKS!
You should check your URL string that you pass in loadImageFromUrl.
Also i think it's better to get as a parameter only URlString and return a completionBlockWith UIImage?
Related
I have tried several different approaches and nothing has yet to work. I am pulling in album artwork for a recently played tableview for my radio station app. I get blank images when there is no album artwork to pull into the cell. I just want to have my station logo "WhiteLogo.png" as a placeholder whenever there is no album artwork pulled into the tableview cell. Any help in the right direction is much appreciated. Thanks
import UIKit
//----------
//MARK: JSON
//----------
//The Initial Response From The JSON
struct Response: Codable {
var playHistory: Album
}
//The Album Received Which Is An Array Of Song Data
struct Album: Codable {
var song: [SongData]
}
//The SongData From The PlayHistory Album
struct SongData: Codable{
var album: String
var artist: String
var cover: String
var duration: String
var programStartTS: String
var title: String
}
class TableViewController: UITableViewController {
//1. Create An Array To Store The SongData
var songs = [SongData]()
var currentStation: RadioStation!
var downloadTask: URLSessionDownloadTask?
override func viewDidLoad() { super.viewDidLoad()
self.tableView.delegate = self
self.tableView.dataSource = self
//2. Load The JSON From The Main Bundle
guard let urlText = URL (string: "http://streamdb3web.securenetsystems.net/player_status_update/JACKSON1_history.txt")
else { return }
do{
//a. Get The Data From The From The File
let data = try Data(contentsOf: urlText)
//b. Decode The Data To Our Structs
let albumData = try JSONDecoder().decode(Response.self, from: data)
//c. Append The Songs Array With The PlayHistory
albumData.playHistory.song.forEach { songs.append($0) }
//d. Test Some Data
print("""
**The First Album Details**
Album = \(songs[0].album)
Artist = \(songs[0].artist)
Cover = \(songs[0].cover)
Duration = \(songs[0].duration)
Start = \(songs[0].programStartTS)
Title = \(songs[0].title)
""")
//3. Load The Data
DispatchQueue.main.async {
self.tableView.reloadData()
}
}catch{
print(error)
}
}
//-----------------
//MARK: UITableView
//-----------------
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return songs.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//1. Create A Cell
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
//2. Set It's Text
cell.songTitle.text = songs[indexPath.row].title
cell.artistLabel.text = songs[indexPath.row].artist
//3. Get The Image
if let imageURL = URL(string: songs[indexPath.row].cover){
let request = URLSession.shared.dataTask(with: imageURL) { (imageData, response, error) in
if let error = error{
print(error)
}else{
guard let image = imageData else { return }
DispatchQueue.main.async {
cell.songCover.image = UIImage(data: image)
cell.setNeedsLayout()
cell.layoutIfNeeded()
}
}
}
request.resume()
}
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("""
**Album \(indexPath.row) Selected**
Album = \(songs[indexPath.row].album)
Artist = \(songs[indexPath.row].artist)
Cover = \(songs[indexPath.row].cover)
Duration = \(songs[indexPath.row].duration)
Start = \(songs[indexPath.row].programStartTS)
Title = \(songs[indexPath.row].title)
""")
}
}
Just the right case handling is required.
I would set the placeholder image first and then proceed to download an image from a URL.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//...
/*
Start with placeholder image so it shows until the image download completes.
And if the next `if let imageURL` condition fails, the placeholder image will stay
*/
cell.songCover.image = UIImage(named: "WhiteLogo")
//Continue with your logic, no change really but could be shortened to:
if let imageURL = URL(string: songs[indexPath.row].cover) {
let request = URLSession.shared.dataTask(with: imageURL) { (imageData, response, error) in
guard let imageData = imageData else { return }
DispatchQueue.main.async {
cell.songCover.image = UIImage(data: imageData)
}
}
request.resume()
}
//...
}
However, since the image download logic is async, it will misbehave if the cell is reused before the download completes.
i.e. Image download for the first song starts but you scroll fast enough to reuse the first cell for, lets say, the third song.
Now, when the download completes, the first image could show on the third cell.
If you face this issue then let me know and I shall update my answer.
Set "WhiteLogo.png" on above your code which download image for album or set logo image if album image data is nil like guard let image = imageData else { var image : UIImage = UIImage(named:"WhiteLogo.png")!
cell.songCover.image = UIImageView(image: image) }
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//1. Create A Cell
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
//2. Set It's Text
cell.songTitle.text = songs[indexPath.row].title
cell.artistLabel.text = songs[indexPath.row].artist
//set image
var image : UIImage = UIImage(named:"WhiteLogo.png")!
cell.songCover.image = UIImageView(image: image)
//3. Get The Image
if let imageURL = URL(string: songs[indexPath.row].cover){
let request = URLSession.shared.dataTask(with: imageURL) { (imageData, response, error) in
if let error = error{
print(error)
}else{
guard let image = imageData else { return }
DispatchQueue.main.async {
cell.songCover.image = UIImage(data: image)
cell.setNeedsLayout()
cell.layoutIfNeeded()
}
}
}
request.resume()
}
return cell
}
guard let image = imageData else { cell.songCover.image = UIImage(named : "your_image_name"); return }
Please use the Kingfisher library it will download image from url and set placeholder image.Library URL:- https://github.com/onevcat/Kingfisher
In my application, I download a JSON file off of the internet and fill up a UITableView with items from the file. It does work well, and there are no problems or errors, but the scrolling performance is very laggy, and the UI glitches out a tiny bit.
I assume this is because of the images that I'm downloading from the JSON file, so I've looked into multi-threading, but I don't think I am doing it right because it does load much faster, but scrolling performance is still the same as before.
Can somebody please tell me how to fix this? This UITableView is the most important thing in the app, and I have been spending much time on trying to fix it. Thank you!
Here is my code-
import UIKit
class ViewController: UIViewController, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var nameArray = [String]()
var idArray = [String]()
var ageArray = [String]()
var genderArray = [String]()
var descriptionArray = [String]()
var imgURLArray = [String]()
let myActivityIndicator = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.gray)
final let urlString = "https://pbsocfilestorage.000webhostapp.com/jsonDogs.json"
override func viewDidLoad() {
super.viewDidLoad()
self.downloadJsonWithURL()
// Activity Indicator
myActivityIndicator.center = view.center
myActivityIndicator.hidesWhenStopped = true
myActivityIndicator.startAnimating()
view.addSubview(myActivityIndicator)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func downloadJsonWithURL() {
let url = NSURL(string:urlString)
URLSession.shared.dataTask(with: (url as? URL)!, completionHandler: {(data, response, error) ->
Void in
print("Good so far...")
if let jsonObj = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSDictionary {
print(jsonObj!.value(forKey: "dogs"))
if let dogArray = jsonObj!.value(forKey: "dogs") as? NSArray {
print("Why u no work!")
for dog in dogArray {
if let dogDict = dog as? NSDictionary {
if let name = dogDict.value(forKey: "name") {
self.nameArray.append(name as! String)
}
if let name = dogDict.value(forKey: "id") {
self.idArray.append(name as! String)
}
if let name = dogDict.value(forKey: "age") {
self.ageArray.append(name as! String)
}
if let name = dogDict.value(forKey: "gender") {
self.genderArray.append(name as! String)
}
if let name = dogDict.value(forKey: "image") {
self.imgURLArray.append(name as! String)
}
if let name = dogDict.value(forKey: "description") {
self.descriptionArray.append(name as! String)
}
OperationQueue.main.addOperation ({
self.myActivityIndicator.stopAnimating()
self.tableView.reloadData()
})
}
}
}
}
}).resume()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return nameArray.count
}
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension;
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let imgURL = NSURL(string: imgURLArray[indexPath.row])
let cell = tableView.dequeueReusableCell(withIdentifier: "reusableCell") as! TableViewCell
URLSession.shared.dataTask(with: (imgURL as! URL), completionHandler: {(data, resp, error) -> Void in
if (error == nil && data != nil) {
OperationQueue.main.addOperation({
cell.dogNameLabel.text = self.nameArray[indexPath.row]
cell.idLabel.text = self.idArray[indexPath.row]
cell.ageLabel.text = self.ageArray[indexPath.row]
cell.genderLabel.text = self.genderArray[indexPath.row]
print("Cell info was filled in!")
if imgURL != nil {
let data = NSData(contentsOf: (imgURL as? URL)!)
cell.dogImage.image = UIImage(data: data as! Data)
}
})
}
}).resume()
return cell
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showDog" {
if let indexPath = self.tableView.indexPathForSelectedRow{
let detailViewController = segue.destination as! DetailViewController
detailViewController.imageString = imgURLArray[indexPath.row]
detailViewController.nameString = nameArray[indexPath.row]
detailViewController.idString = idArray[indexPath.row]
detailViewController.ageString = ageArray[indexPath.row]
detailViewController.descriptionString = descriptionArray[indexPath.row]
detailViewController.genderString = genderArray[indexPath.row]
}
}
}
}
There is a big mistake. You are loading data with dataTask but you aren't using that returned data at all. Rather than you are loading the data a second time with synchronous contentsOf. Don't do that.
And don't update the labels in the asynchronous completion block. The strings are not related to the image data.
This is more efficient:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let imgURL = URL(string: imgURLArray[indexPath.row])
let cell = tableView.dequeueReusableCell(withIdentifier: "reusableCell", for: indexPath) as! TableViewCell
cell.dogNameLabel.text = self.nameArray[indexPath.row]
cell.idLabel.text = self.idArray[indexPath.row]
cell.ageLabel.text = self.ageArray[indexPath.row]
cell.genderLabel.text = self.genderArray[indexPath.row]
print("Cell info was filled in!")
URLSession.shared.dataTask(with: imgURL!) { (data, resp, error) in
if let data = data {
OperationQueue.main.addOperation({
cell.dogImage.image = UIImage(data: data)
})
}
}.resume()
return cell
}
Note: You are strongly discouraged from using multiple arrays as data source. It's very error-prone. Use a custom struct or class. And create imgURLArray with URL instances rather than strings. This is also much more efficient.
Nevertheless, you should use a download manager which caches the images and cancels downloads if a cell goes off-screen. At the moment each image is downloaded again when the user scrolls and cellForRow is called again for this particular cell.
I have one collection view and it have some 5 cells. Which are I am fetching from api to show the cells data in collection view. And when user click any cell, that approprate data will display in below table view.
Now, every thing works fine. But when I load first time my view. No data are displaying in my table view... What I mean is? By default first data of collection view cell have to show in tableview. But I am not getting or not able to display.
If I click any cell only, I can see the data in my table view. But what I need is - By default first cell of collection view have to display in table view when ever i open that screen
How to do that?
Here is my code:
#IBOutlet var BTCollectionView: UICollectionView!
#IBOutlet var DLTableView: UITableView!
var BTdata = [BTData]()
var Dealsdata = [DealsData]()
override func viewDidLoad()
{
super.viewDidLoad()
// nib for custom cell (table view)
let nib = UINib(nibName:"DealsListTableCell", bundle: nil)
DLTableView.registerNib(nib, forCellReuseIdentifier: "DealCell")
ListBusinessTypes()
}
// Values from Api for Business Types
func ListBusinessTypes()
{
let token = NSUserDefaults.standardUserDefaults().valueForKey("access_token") as! String
let headers = ["x-access-token": token]
let request = NSMutableURLRequest(URL: NSURL(string: "httpsome url")!,
cachePolicy: .UseProtocolCachePolicy,
timeoutInterval: 10.0)
request.HTTPMethod = "GET"
request.allHTTPHeaderFields = headers
let session = NSURLSession.sharedSession()
let dataTask = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in
if (error != nil)
{
print(error)
let ErrorAlert = UIAlertController(title: "Error", message: "Problem with internet connectivity or server, please try after some time", preferredStyle: UIAlertControllerStyle.Alert)
// add an action (button)
ErrorAlert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
// show the alert
self.presentViewController(ErrorAlert, animated: true, completion: nil)
}
else
{
if let json = (try? NSJSONSerialization.JSONObjectWithData(data!, options: [])) as? Dictionary<String,AnyObject>
{
let success = json["success"] as? Int
if(success == 1)
{
if let typeValues = json["data"] as? [NSDictionary]
{
dispatch_async(dispatch_get_main_queue(),{
for item in typeValues
{
self.BTdata.append(BTData(json:item))
self.BTCollectionView.reloadData()
}
})
}
}
else
{
let message = json["message"] as? String
print(message)
let ServerAlert = UIAlertController(title: "Error", message: message, preferredStyle: UIAlertControllerStyle.Alert)
// add an action (button)
ServerAlert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
// show the alert
self.presentViewController(ServerAlert, animated: true, completion: nil)
}
}
}
})
dataTask.resume()
}
// Mark : Collection View Delegate and Datasource(Business Type)
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
return BTdata.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell
{
let cell: DDLCollectionCell = collectionView.dequeueReusableCellWithReuseIdentifier("HCollectionCell", forIndexPath: indexPath) as! DDLCollectionCell
cell.BTName.text = BTdata[indexPath.row].BTNames
return cell
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath)
{
ListDeals(BTdata[indexPath.row].BTIds!)
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int
{
return 1
}
// number of rows
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return Dealsdata.count
}
// calling each cell based on tap and users ( premium / non premium )
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let tabcell = tableView.dequeueReusableCellWithIdentifier("DealCell") as! DealsListTableCell
tabcell.DealName.text = Dealsdata[indexPath.row].DealNames
if let imgURL = NSURL(string: Dealsdata[indexPath.row].DealImageUrls!)
{
let request: NSURLRequest = NSURLRequest(URL: imgURL)
let session = NSURLSession.sharedSession()
let Imgtask = session.dataTaskWithRequest(request)
{
(data, response, error) -> Void in
if (error == nil && data != nil)
{
func display_image()
{
tabcell.DealImage.image = UIImage(data: data!)
}
dispatch_async(dispatch_get_main_queue(), display_image)
}
}
Imgtask.resume()
}
else
{
tabcell.DealImage.image = UIImage(named: "FBLogo")
}
let formatter = NSDateFormatter()
formatter.locale = NSLocale(localeIdentifier: "en_US")
formatter.dateFormat = "yyyy-MM-dd'T'hh:mm:ss.SSSSxxx"
let date = formatter.dateFromString(Dealsdata[indexPath.row].DealExpiry!)
let dateFormatter = NSDateFormatter()
dateFormatter.dateStyle = .MediumStyle
dateFormatter.timeStyle = .NoStyle
dateFormatter.locale = NSLocale(localeIdentifier: "en_US")
let FormattedDate = dateFormatter.stringFromDate(date!)
tabcell.RegPriceLabel.text = String(Dealsdata[indexPath.row].DealRegularPrice!)
tabcell.SalePriceLabel.text = String(Dealsdata[indexPath.row].DealSalePrice!)
tabcell.DealExpiryDate.text = "Expiries on : "+FormattedDate
let BArrayValue:NSDictionary = Dealsdata[indexPath.row].DealBusinessDetails!
let BName = BArrayValue.valueForKey("business_name") as! String
let BImage = BArrayValue.valueForKey("images") as! NSArray
let BMainImage = BImage[0] as! NSDictionary
let FinalImage = BMainImage.valueForKey("url") as! String
if let imgURL2 = NSURL(string: FinalImage)
{
let request: NSURLRequest = NSURLRequest(URL: imgURL2)
let session = NSURLSession.sharedSession()
let Imgtask = session.dataTaskWithRequest(request)
{
(data, response, error) -> Void in
if (error == nil && data != nil)
{
func display_image()
{
tabcell.DealBusinessImage.image = UIImage(data: data!)
}
dispatch_async(dispatch_get_main_queue(), display_image)
}
}
Imgtask.resume()
}
else
{
tabcell.DealBusinessImage.image = UIImage(named: "FBLogo")
}
let BLatLng = Dealsdata[indexPath.row].DealCoordinates
let UserLocation = CLLocation(latitude: NewCurrentLatitude, longitude: NewCurrentLongitude)
let BusinessLocation = CLLocation(latitude: BLatLng![0] as! Double, longitude: BLatLng![1] as! Double)
let distance = UserLocation.distanceFromLocation(BusinessLocation) / 1000
tabcell.DealBusinessNameWithDistance.text = BName+" - "+String(format: "%.1f",distance)+" Km"
return tabcell
}
Please help me out. Thanks!
Does i need to change any thing in below line:
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath)
{
ListDeals(BTdata[indexPath.row].BTIds!)
}
Try to call ListDeals(BTdata[0].BTIds!) after you loaded the data from your API.
...
if let typeValues = json["data"] as? [NSDictionary]
{
dispatch_async(dispatch_get_main_queue(),{
for item in typeValues
{
self.BTdata.append(BTData(json:item))
self.BTCollectionView.reloadData()
}
ListDeals(BTdata[0].BTIds!)
})
}
...
I want to use UICollectionView for displaying the images and I am getting that images by api calling.
Question: so I am getting images path via api calling so how can I display it to UICollectionView??
here is my code ::
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let dic = imagearray .objectAtIndex(indexPath.row) as! NSDictionary
let cell :imagecell = collectionView.dequeueReusableCellWithReuseIdentifier("imagecell", forIndexPath: indexPath) as! imagecell
cell.imagev.image = dic["image"] as? UIImage
return cell
}
and here is my api response
(
{
image = "http://radio.spainmedia.es/wp-content/uploads/2015/12/esquire.jpg";
slug = esquire;
},
{
image = "http://radio.spainmedia.es/wp-content/uploads/2015/12/forbes.jpg";
slug = forbes;
},
{
image = "http://radio.spainmedia.es/wp-content/uploads/2015/12/tapas.jpg";
slug = tapas;
}
)
so how can I display this images in my UICollectionView
UPDATE:: While using commented code getting strange issue i am calling my webservice in viewdidload
override func viewDidLoad() {
super.viewDidLoad()
webimages()
// Do any additional setup after loading the view, typically from a nib.
}
and its started to call webservice
func webimages()
{
let url = "http://radio.spainmedia.es/podcasts/"
request(.GET, url, parameters: nil, encoding: .JSON).responseJSON { (response:Response<AnyObject, NSError>) -> Void in
print(response.result.value)
self.imagearray = (response.result.value) as! NSMutableArray
print(self.imagearray)
}
}
but after requesting its suddenly go to cellForItemAtIndexPath so my "imagearray" found nil there. and then its comeback to webimages() and giving me api response.
So how can I solve this?
we have array of string we are passing single string here so can you please tell me that what is the solution
We have array of string we are passing single string here so can you please tell me that what is the solution
enter image description here
You are setting a URL string as UIImage. You first have to retrieve image from that URL first. Use the following method for quick remedy:
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell :imagecell = collectionView.dequeueReusableCellWithReuseIdentifier("imagecell", forIndexPath: indexPath) as! imagecell
if imagearray.count > 0
{
let dic = imagearray .objectAtIndex(indexPath.row) as! NSDictionary
let imgURL: NSString = dic!["image"] as! NSString //Get URL string
let url = NSURL.URLWithString(imgURL); //Create URL
var err: NSError?
var imageData :NSData = NSData(contentsOfURL: url, options: NSDataReadingOptions.DataReadingMappedIfSafe, error: &err)! //Fetch Image Data
var cellImage = UIImage(data:imageData) //Create UIImage from Image data
cell.imagev.image = cellImage //Set image
}
return cell
}
Notice that this is fetching content of image URL in a synchronous call so that would freeze your UI until download completes. Also this is not caching the Image so images will be downloaded over and over again when you scroll and cells are recreated. To avoid that I'd suggest caching .
For better results, This is how you load image asynchronously, without freezing the UI and cache the images to avoid network load.
You first have to create a class first like this:
class ImageLoader {
var cache = NSCache() //Create cache
class var sharedLoader : ImageLoader {
struct Static {
static let instance : ImageLoader = ImageLoader()
}
return Static.instance
}
func imageForUrl(urlString: String , indexPathArg:NSIndexPath!, completionHandler:(image: UIImage?, url: String,indexPathResponse:NSIndexPath?) -> ()) {
let currentIndexPath: NSIndexPath! = indexPathArg.mutableCopy() as! NSIndexPath
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), {()in
let data: NSData? = self.cache.objectForKey(urlString) as? NSData
//Check if image data for this URL already exists in Cache
if let goodData = data {
//data exists, no need to download it again. Just send it
let image = UIImage(data: goodData)
dispatch_async(dispatch_get_main_queue(), {() in
completionHandler(image: image, url: urlString,indexPathResponse: currentIndexPath)
})
return
}
//Data does not exist, We have to download it
let downloadTask: NSURLSessionDataTask = NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: urlString)!,completionHandler: { (data: NSData?, response:NSURLResponse?, error: NSError?) -> Void in
if (error != nil) {
//Download failed
completionHandler(image: nil, url: urlString, indexPathResponse: currentIndexPath)
return
}
if data != nil {
//Download successful,Lets save this downloaded data to our Cache and send it forward as UIImage
let image = UIImage(data: data!)
self.cache.setObject(data!, forKey: urlString)
dispatch_async(dispatch_get_main_queue(), {() in
completionHandler(image: image, url: urlString, indexPathResponse: currentIndexPath)
})
return
}
})
downloadTask.resume()
})
}
}
Then you have to modify your collectionview delegate like this:
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell :imagecell = collectionView.dequeueReusableCellWithReuseIdentifier("imagecell", forIndexPath: indexPath) as! imagecell
if imagearray.count > 0
{
let dic = imagearray .objectAtIndex(indexPath.row) as! NSDictionary
let imgURL: NSString = dic!["image"] as! NSString//Get URL string
ImageLoader.sharedLoader.imageForUrl(imgURL as String,indexPathArg: indexPath, completionHandler:{(image: UIImage?, url: String, indexPathResponse: NSIndexPath?) in
let indexArr:NSArray = collectionView!.indexPathsForVisibleItems()
if indexArr.containsObject(indexPathResponse!)
{
cell.imagev.image = image //Set image
}
})
}
return cell
}
Now it will load your image asynchronously and will download it only if necessary. Great Success! (To quote Borat). I have added comments so that you can understand What's going on in my code and Daniel's code :)
To Fix your crash issue which is not a part of your original question and instead a different problem you created, Return count of items in section to be count of your image array and reload collectionview once you have retrieved your data:
func webimages()
{
let url = "http://radio.spainmedia.es/podcasts/"
request(.GET, url, parameters: nil, encoding: .JSON).responseJSON { (response:Response<AnyObject, NSError>) -> Void in
print(response.result.value)
self.imagearray = (response.result.value) as! NSMutableArray
print(self.imagearray)
//Reload Collection view
self.collectionView?.reloadData()
}
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return imagearray.count
}
Credits for Imageloader class: Daniel Sattler
Special Thanks to: CouchDeveloper
Pretty easy you got to downlaod the image from that url and set it as the image for the image view,
Try this, https://github.com/natelyman/SwiftImageLoader
Add the ImageLoader class to your project and modify the collectionview data source as below,
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let dic = imagearray .objectAtIndex(indexPath.row) as! NSDictionary
let cell :imagecell = collectionView.dequeueReusableCellWithReuseIdentifier("imagecell", forIndexPath: indexPath) as! imagecell
//cell.imagev.image = dic["image"] as? UIImage
ImageLoader.sharedLoader.imageForUrl(dic["image"], completionHandler: {(image: UIImage?, url: String) in
cell.imagev.image = image
})
return cell
}
This is an asynchronous image loading class so UI would not freeze or give you any other problems if you are against using any third party libs or classes please do it manually as #NSNoob 's answer.
Other good image loading libraries are,
https://github.com/nicklockwood/AsyncImageView
https://github.com/onevcat/Kingfisher
https://github.com/MengTo/Spring/blob/master/Spring/AsyncImageView.swift
https://github.com/anas10/AsyncImageView-Swift
You can extend UIImageView as following -
extension UIImageView {
public func imageFromU(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)
}
}
}
}
}
Then in any UIImageView you will have a very simple helper method as follows -
yourImageView.imageFromURL("https://yoururl.com/image.png")
And in your case
cell.imagev.image.imageFromURL(dic["image"])
if let url = NSURL(string: "http://www.apple.com/euro/ios/ios8/a/generic/images/og.png") {
if let data = NSData(contentsOfURL: url){
imageURL!.contentMode = UIViewContentMode.ScaleAspectFit
imageURL!.image = UIImage(data: data)
}
}
I've been trying to return string urls from JSON and store it in array and then show the array in UITableView. but it shows empty UILabel.
class PhotosTableViewController: UITableViewController {
let imageLoadURL = "https://..."
var TAG_IMG_URL = []
verride func viewDidLoad() {
super.viewDidLoad()
getLatestPhotos()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return TAG_IMG_URL.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! KivaLoanTableViewCell
cell.nameLabel.text = TAG_IMG_URL[indexPath.row] as? String
return cell
}
func getLatestPhotos() {
let request = NSURLRequest(URL: NSURL(string: imageLoadURL)!)
let urlSession = NSURLSession.sharedSession()
let task = urlSession.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in
if error != nil {
println(error.localizedDescription)
}
self.TAG_IMG_URL = self.parseJsonData(data)
println("\(self.TAG_IMG_URL.count)")
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
})
})
task.resume()
}
func parseJsonData(data: NSData) -> NSArray {
var error:NSError?
let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &error) as? NSDictionary
if error != nil {
println(error?.localizedDescription)
}
if let j = jsonResult, let mediaObjects = j.valueForKeyPath("feed.entry.media$group.media$content") as? NSArray {
if let imageUrls: AnyObject = mediaObjects.valueForKey("url") {
TAG_IMG_URL = imageUrls as! NSArray
}
}
println("\(TAG_IMG_URL)")
self.alert.dismissWithClickedButtonIndex(0, animated: true)
return TAG_IMG_URL
}
}
During parseJsonData it returns the urls which it look like (below), but when i try to show it in the UITableView it always becomes empty UILabel so what am i doing wrong here ?:
(
(
"https://..."
),
(
"https://..."
)
)
Note : in numberOfRowsInSection it returns the right amount which its 2 urls.
Try this:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! KivaLoanTableViewCell
cell.nameLabel.text = TAG_IMG_URL[indexPath.row][0] as? String
return cell
}
The problem that you have 2-dimensional array, so you should get first object in object:
TAG_IMG_URL[indexPath.row].firstObject or TAG_IMG_URL[indexPath.row][0].