I am creating a small app for displaying images in TableView from Flickr. The app works fine but i am facing a issue while scrolling table view, my tableview lags a lot. I think the issue might be with GCD or Threads, i am new to networking and GCD, this is my code getting images from Flickr API.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = flickrTableView.dequeueReusableCellWithIdentifier("cell") as! FlickerTableCell
let flickr = flickrArray[indexPath.row]
dispatch_async(dispatch_get_main_queue(), {
cell.imageViewFlickr.image = flickr.getFlickrImage()
})
cell.labelFlickrTitle.text = flickr.title
return cell
}
// function to get images from flickr
func getFlickrImage()->UIImage{
let imageURL = NSURL(string: "https://farm\(farm).staticflickr.com/\(server)/\(photoId)_\(secret)_m.jpg")!
var flickrImage = UIImage()
if let imageData = NSData(contentsOfURL: imageURL) {
flickrImage = UIImage(data: imageData)!
} else {
print("Image does not exist at \(imageURL)")
}
return flickrImage
}
Your method to fetch image from network that is getFlickrImage() is sync method so table cell waits for its response and as the network call is in main thread it freezes or lags the UI. you can wrap your network call in async method and use completion handler to update the UI in main thread such as:
func getFlickrImage(completion:UIImage ->()){
let imageURL = NSURL(string: "https://farm\(farm).staticflickr.com/\(server)/\(photoId)_\(secret)_m.jpg")!
var flickrImage = UIImage()
let download = dispatch_queue_create("download", nil)
dispatch_async(download) {
if let imageData = NSData(contentsOfURL: imageURL) {
dispatch_async(dispatch_get_main_queue(), {
flickrImage = UIImage(data: imageData)!
completion(flickrImage)
})
} else {
print("Image does not exist at \(imageURL)")
completion(flickrImage)
}
}
}
Your cellForRowAtIndexPath will be like:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = flickrTableView.dequeueReusableCellWithIdentifier("cell") as! FlickerTableCell
let flickr = flickrArray[indexPath.row]
flickr.getFlickrImage { (photo) in
dispatch_async(dispatch_get_main_queue(), {
cell.imageViewFlickr.image = photo
})
}
cell.labelFlickrTitle.text = flickr.title
return cell
}
Related
My images take a second to load before they appear, which looks bad. On apps such as instagram, the tableview is hidden until the tableview is loaded... how do they do this? I have a loader that I want to display but don't know when to stop it and show tableview and detect the images have first finished loading? Where do I put stopTimer() ?
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MainTableViewCell",
for: indexPath) as! MainTableViewCell
let payment = self.payments[indexPath.row]
cell.profilePicture.layer.cornerRadius = cell.profilePicture.frame.size.width / 2
cell.profilePicture.clipsToBounds = true
if let profileImageUrl = payment.picture {
cell.profilePicture.loadImageUsingCacheWithUrlString(profileImageUrl)
}
if payment.message == "none" {
cell.detailsLabel.text = "No Message"
} else {
cell.detailsLabel.text = "\"\(payment.message ?? "")\""
}
}
MY CODE TO FETCH IMAGE IN TABLEVIEW:
let imageCache = NSCache<NSString, AnyObject>()
extension UIImageView {
func loadImageUsingCacheWithUrlString(_ urlString: String) {
self.image = nil
//check cache for image first
if let cachedImage = imageCache.object(forKey: urlString as NSString) 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 let error = error {
print(error)
return
}
DispatchQueue.main.async(execute: {
if let downloadedImage = UIImage(data: data!) {
imageCache.setObject(downloadedImage, forKey: urlString as NSString)
self.image = downloadedImage
}
})
}).resume()
}
}
You can simple use SDWebImage with cocoaPods and use it for async image downloader with cache support. Your cell will look like after ad SDWebImage.
import SDWebImage
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MainTableViewCell",
for: indexPath) as! MainTableViewCell
let payment = self.payments[indexPath.row]
cell.profilePicture.layer.cornerRadius = cell.profilePicture.frame.size.width / 2
cell.profilePicture.clipsToBounds = true
if let profileImageUrl = payment.picture {
cell.profilePicture.sd_setImage(with: profileImageUrl, placeholderImage: UIImage(named: "yourPlaceholderImage.png"))
}
if payment.message == "none" {
cell.detailsLabel.text = "No Message"
} else {
cell.detailsLabel.text = "\"\(payment.message ?? "")\""
}
}
There is no need to hide tableView for downloading image.
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
I have a TableView and I am filling it with data retrieved from database. Everything works fine except the images. Because of the cell reuse behaviour, and I am fetching image in cellForRowAtIndexPath. I chose to fetch images in cellForRowAtIndexPath because in the details retrieving function (which is triggered in viewDidLoad), I need to do another request, which is causing other problems (reloading tableview before storing image url)
The problem is that when I scroll fast, the resuable cells bugs while displaying user images
override func viewDidLoad() {
super.viewDidLoad()
fetchData()
}
var theUser =
func fetchData() {
//.. after data is retrieved
var innerDict = [String:String]()
if let user = details.value![key] {
if let name = user["name"] {
// works
innerDict["name"] = name
}
if let image = user["imageName"] {
// gets the image name but at this point I need to;
// a) retrieve the url here (with another call), which will eventually fail
// to catch up with `innerDict` so `innerDict` won't contain `image` variable.
// ie;
myRef.downloadURLWithCompletion { (URL, error) -> Void in }
// b) Store the `image` name in innerDict and download image from url
// in `cellForRowAtIndexPath`. I chose this method:
innerDict["image"] = image
}
user[id] = innerDict
tableView.reloadData()
}
Now the tableView as usual.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = ...
// more stuff
if let imageName = user["image"] {
let storage = FIRStorage.storage()
let storageRef = storage.referenceForURL("gs://bucket.com").child(user[id]).child(imageName)
storageRef.downloadURLWithCompletion { (URL, error) -> Void in
if (error != nil) {
// handle
} else {
// I thought using Haneke would help to cache the image
cell.image.hnk_setImage(URL!)
}
}
}
This is the closest one I could reach. However images bug on displaying when I scroll fast.
Edit:
I also tried using this approach but it's downloading the same image multiple times with this method, so it takes time for the same images to displayed.
islandRef.dataWithMaxSize(1 * 1024 * 1024) { (data, error) -> Void in
if (error != nil) {
// Uh-oh, an error occurred!
} else {
let image: UIImage! = UIImage(data: data!)
cell.userImage.hnk_setImage(image, key: "\(userID)")
}
}
However, with top approach the speed was very fast. The only problem of the above code was the glitch when I scroll fast.
Edit 2
var images = [UIImage]()
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("ItemCell", forIndexPath: indexPath) as! ItemDetailTableViewCell
let item = items[indexPath.section][indexPath.row]
if let uid = item["owner"] as? String {
if let user = users[uid] {
if let imageName = user["image"] {
if let img: UIImage = images[indexPath.row] { // crash here "fatal error: Index out of range"
cell.userImage.image = img
}
} else {
let storage = FIRStorage.storage()
let storageRef = storage.referenceForURL("gs://bucket").child(uid).child(imageName)
storageRef.downloadURLWithCompletion { (URL, error) -> Void in
if (error != nil) {
} else {
dispatch_async(dispatch_get_main_queue(), {
cell.userImage.hnk_setImageFromURL(URL!)
self.images[indexPath.row] = cell.image.image!
})
}
}
}
}
}
I think you should save images from url and show images when the cell is going to reuse, hopefully this will fix your glitch
var myImages = [String: UIImage]()
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("ItemCell", forIndexPath: indexPath) as! ItemDetailTableViewCell
let item = items[indexPath.section][indexPath.row]
if let img: UIImage = myImages["\(indexPath.section)\(indexPath.row)"] {
cell.image.image = img
} else {
if let uid = item["owner"] as? String {
if let imageName = user["image"] {
let storage = FIRStorage.storage()
let storageRef = storage.referenceForURL("gs://bucket.com").child(user[id]).child(imageName)
storageRef.downloadURLWithCompletion { (URL, error) -> Void in
if (error != nil) {
cell.image = UIImage(named: "placeholder") // put default Image when failed to download Image
} else {
dispatch_async(dispatch_get_main_queue(), {
cell.image.hnk_setImage(URL!)
// Store the image in to our cache
self.myImages["\(indexPath.section)\(indexPath.row)"]= cell.image.image
})
}
}
}
}
}
I have an async problem - I have a function that loads images from S3, stores them into an array of UIImages (here called Images)
I also have a tableview that loads its cells from firebase fetched data, my question is, how to update the cell image once the async finishes loading ?
I'm also afraid that the queue of images won't match exactly the indexPath.row since some images might load faster than other images.
func download(key:String, myindex:NSIndexPath, myrow:Int) -> NSString {
let path:NSString = NSTemporaryDirectory().stringByAppendingString("image.jpg")
let url:NSURL = NSURL(fileURLWithPath: path as String)
// let downloadingFilePath = downloadingFileURL.path!
let downloadRequest = AWSS3TransferManagerDownloadRequest()
downloadRequest.bucket = "witnesstest/" + rootref.authData.uid
downloadRequest.key = key
downloadRequest.downloadingFileURL = url
switch (downloadRequest.state) {
case .NotStarted, .Paused:
let transferManager = AWSS3TransferManager.defaultS3TransferManager()
transferManager.download(downloadRequest).continueWithBlock({ (task) -> AnyObject! in
if let error = task.error {
if error.domain == AWSS3TransferManagerErrorDomain as String
&& AWSS3TransferManagerErrorType(rawValue: error.code) == AWSS3TransferManagerErrorType.Paused {
print("Download paused.")
} else {
print("download failed: [\(error)]")
}
} else if let exception = task.exception {
print("download failed: [\(exception)]")
} else {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
let tempimage:UIImage = UIImage(contentsOfFile: path as String)!
print("dl ok")
self.Images.append(tempimage)
})
}
return nil
})
break
default:
break
}
return path
}
and the cell :
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: MainWitnessTableViewCell = self.tableView.dequeueReusableCellWithIdentifier("RIcell") as! MainWitnessTableViewCell
// populate the cell
let postitem = posts[indexPath.row]
cell.postOwner.text = postitem.author
cell.postContent.text = postitem.content
cell.postDate.text = postitem.createon
let myindex = indexPath
let myrow = indexPath.row
// cell.cellImage.image = Images[indexPath.row] // returns array index out of range
// download(postitem.imagekey, myindex: myindex, myrow: myrow)
return cell
}
You can just set the imageView's image after you download the image and at the same time set postitem's image property (assuming you add one). It's hard for me to understand everything your download method is doing, but I think the gist of what you want is something like:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: MainWitnessTableViewCell = self.tableView.dequeueReusableCellWithIdentifier("RIcell") as! MainWitnessTableViewCell
// populate the cell
let postitem = posts[indexPath.row]
cell.postOwner.text = postitem.author
cell.postContent.text = postitem.content
cell.postDate.text = postitem.createon
//postitem should also have a imageURL property or you should have some way of getting the image url.
if post item.image == nil{
dispatch_queue_t imageFetchQ = dispatch_queue_create("image fetcher", NULL)
dispatch_async(imageFetchQ, ^{
let imageData = NSData(contentsOfURL: postitem.imageURL)
let image = UIImage(data: imageData)
postitem.image = image
dispatch_async(dispatch_get_main_queue(), ^{
//the UITableViewCell may have been dequeued and reused so check if the cell for the indexPath != nil
if tableView.cellForRowAtIndexPath(indexPath) != nil{
cell.imageView.image = image
}
})
})
return cell
}
I would recommend you create a cache of images and the indices they're supposed to be associated with, and then instead of loading all the images in your function, you would create the table view, and then for that specific cell ask for the image from your server or from the cache if it's already there, rather then trying to download them all async at one time
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)
}
}