Using SDWebImage to copy an image as NSData to Pasteboard - swift

I'm using SDWebImage to load images into a UICollectionView. Then when the user clicks on the image I want to copy it to the Pasteboard so they can paste it into other apps.
The code I am currently using in the didSelectItemAtIndexPath method goes back to the web to copy the image. But the image should already be in the user's cache.
How do I use SDWebImage to grab an image for NSData?
That way the app will first check the cache for the image. I keep running into DataType issues when I try to use SDWebImage.
This is my current code. I want to fix the code in the didSelectItemAtIndexPath:
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let iCell = collectionView.dequeueReusableCellWithReuseIdentifier("ImageCollectionViewCell", forIndexPath: indexPath) as! ImageCollectionViewCell
let url = NSURL(string: self.imgArray.objectAtIndex(indexPath.row) as! String)
iCell.imgView.sd_setImageWithURL(url)
return iCell
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
// Change code below to use SDWebImage
let url = NSURL(string: self.imgArray.objectAtIndex(indexPath.row) as! String)
let data = NSData(contentsOfURL: url!)
if data != nil {
UIPasteboard.generalPasteboard().setData(data!, forPasteboardType: kUTTypePNG as String)
} else {
// Do nothing
}
}

SDWebImage has a cache. You can access it with SDImageCache. There is customization allowed (different namespaces for caches).
let image = SDImageCache.sharedImageCache.imageFromDiskCacheForKey(url!)‌
if image != nil
{
let data = UIImagePNGRepresentation(image);
}
You have to check if image is not nil because the downloads are asynchrone. So user may trigger collectionView:didSelectItemAtIndexPath: whereas the image hasn't been downloaded yet, and per se not present in the cache.

Related

Proper method to Iterate and Load Images and title from Array (image_url) into ViewController in Swift

I using a CollectionView with an ImageView in the prototype cell. I retrieve API result (title and image_url) and I would like to display an image and title in the collection view through array to retrieve the title and image_url.
Is it possible to retrieve image_url and display the image in the CollectionView?
I used SwiftyJSON to create the array and I can retrieve the API result (included title and image_url) in the array (can pint out the result). But I can't show it in the collection view.
I am posting an example code for your problem that might help you.
// Array that you retrieve from your API
let titleAndImageArray = []()
//CollectionView DataSourceMethod
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return titleAndImageArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell:YourCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! YourCollectionViewCell
let labelTitle = titleAndImageArray[indexPath.row].title
let musicImageUrlString = titleAndImageArray[indexPath.row].image_url
cell.yourLabelOutletName.text = labelTitle
loadImage(indexPath: indexPath, cell: cell, urlString:musicImageUrlString)
}
*I have created a loadImage function to get image from URL which uses SDWebImage library which is Asynchronous image downloader SDWebImage *
func loadImage(indexPath:IndexPath,cell:YourCollectionViewCell,urlString:String){
cell.yourImageViewOutletName.sd_setImage(with: URL(string: urlString), placeholderImage: UIImage(named: "App-Default"),options: SDWebImageOptions(rawValue: 0), completed: { (image, error, cacheType, imageURL) in
})
}

How to pass download image to another view controller

I'm currently learning iOS development and was wondering how I would go about passing a downloaded image to another view controller to be displayed there after tapping on a cell. So, a user would tap on a cell that has an image and then another view controller would pop up with that image. I downloaded the image using URLSession and then cached the image so that the user wouldn't have download the image every time they scroll back up or down to another cell that they've already seen. My issue now is how would I get that downloaded image to another view controller? I tried this, but videoController.thumbnailImageView.image is still coming back as nil.
let imageCache = NSCache<AnyObject, AnyObject>()
override func collectionView(_ collectionView: UICollectionView,
didSelectItemAt indexPath: IndexPath) {
let videoController = VideoController()
let videoUrl = URL(string: Videos[indexPath.item].thumbnail_image_name)
videoController.titleLabel.text = Videos[indexPath.item].title
videoController.thumbnailImageView.image = self.imageCache.object(forKey: videoUrl as AnyObject) as? UIImage
show(videoController, sender: Videos[indexPath.item])
}
Here is how I'm caching the images in cellForItemAt indexPath function.
DispatchQueue.main.async {
let imageToCache = UIImage(data: data!)
self.imageCache.setObject(imageToCache!, forKey: urlString as AnyObject)
cell.thumbnailImageView.image = UIImage(data: data!)
}
I figured it out. For anyone wondering you have to do this:
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath) as! VideoCell
let videoController = VideoController()
videoController.titleLabel.text = Videos[indexPath.item].title
videoController.thumbnailImageView.image = cell.thumbnailImageView.image
show(videoController, sender: self)
}
You need to create the cell for the cell item at that selected Index path. I'm not sure why the cached way didn't work, but when I found out I will update this question for anyone else wondering.

UICollectionView cell images keep changing while scrolling

I am using CollectionView to display images of gif and png format. All the gif images are loading properly but when I scroll the CollectionView, the png images keep being replaced by other gif images.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "StickerCell", for: indexPath)as! StickerCell
let stickerString = String(describing: self.stickerArray[indexPath.row])
if (stickerString.hasSuffix(".gif")){
let url = self.stickerArray[indexPath.row]
if let data = try? Data(contentsOf: url){
let image = UIImage()
image.setGifFromData(data, levelOfIntegrity: 0.5)
cell.sticker.setGifImage(image)
}
} else {
let url = self.stickerArray[indexPath.row]
if let data = try? Data(contentsOf: url){
let image: UIImage = UIImage(data: data)!
cell.sticker.image = image
}
}
return cell
}
I have also tried using DispatchQueue.main.async, its not working either.
I don't think it's a good idea to download the image data every time a collection view cell is loaded. Try using https://github.com/onevcat/Kingfisher for downloading and caching images. You need to download the images once and then images will get cached, which it means next time you scroll it will use cached images and you will not get anymore this issue.

Async images change every time while scrolling?

So I'm creating an iOS app that lets you browse through the Unsplash wallpapers and I used UICollectionView to load the images in cells but whenever I scroll through an image, I go back the image changes into a different one.
Here's the code
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! ImageCollectionViewCell
let downloadQueue = dispatch_queue_create("com.donbytyqi.Papers", nil)
dispatch_async(downloadQueue) {
let imageURL = NSURL(string: "https://unsplash.it/200/300/?random")
let imageData = NSData(contentsOfURL: imageURL!)
var image: UIImage?
if imageData != nil {
image = UIImage(data: imageData!)
}
dispatch_async(dispatch_get_main_queue()) {
cell.imageView.image = image
}
}
return cell
}
EDIT: Two things going on:
collectionView.dequeueReusableCellWithReuseIdentifier reuses a cell that has already been created (if there's one available). So you're dequeueing one of your previous cells.
The URL your loading your images from generates a random image each time it is called.
Thus, when you scroll to the point where the first row of your collectionview is off screen, those cells get reused. Then when you scroll back up, the cells are recreated with a new random image from "https://unsplash.it/200/300/?random"
A way of circumventing this would be to keep an array of all your images indexed based on the cell index. Of course, if your images are very big and/or you have a really large collectionView, you may run out of memory.
Take a look at this code that I have mocked up. I have not verified that the code actually works.
//instance var to store your images
var imageArray: [UIImage]?
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! ImageCollectionViewCell
// Check if we have already loaded an image for this cell index
if let oldImage: UIImage = imageArray[indexPath.row] {
cell.imageView.image = oldImage
return cell
} else {
// remove the old image, before downloading the new one
cell.imageView.image = nil
}
let downloadQueue = dispatch_queue_create("com.donbytyqi.Papers", nil)
dispatch_async(downloadQueue) {
let imageURL = NSURL(string: "https://unsplash.it/200/300/?random")
let imageData = NSData(contentsOfURL: imageURL!)
var image: UIImage?
if imageData != nil {
image = UIImage(data: imageData!)
// Save image in array so we can access later
imageArray.insert(image, atIndex: indexPath.row)
}
dispatch_async(dispatch_get_main_queue()) {
cell.imageView.image = image
}
}
return cell
}
#toddg solution is correct. But still it have a problem in reusing the cell.
If the cell is reused before the network call completion then it will assign the downloaded image to another cell.
So I changed the code like following.
var imageArray: [UIImage]?
let downloadQueue = dispatch_queue_create("com.donbytyqi.Papers", nil)
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! ImageCollectionViewCell
if let oldImage: UIImage = imageArray[indexPath.row] {
cell.imageView.image = oldImage
return cell
} else {
cell.imageView.image = nil;
downloadImage(indexPath);
}
return cell
}
func downloadImage(indexPath: NSIndexPath) {
dispatch_async(downloadQueue) {
let imageURL = NSURL(string: "https://unsplash.it/200/300/?random")
let imageData = NSData(contentsOfURL: imageURL!)
var image: UIImage?
if imageData != nil {
image = UIImage(data: imageData!)
}
let cell = self.collectionView .cellForItemAtIndexPath(indexPath) as! ImageCollectionViewCell
dispatch_async(dispatch_get_main_queue()) {
cell.imageView.image = image
}
}
}
Hope this helps.
Let me explain what is going on actually.
When you scroll and go back you actually see the previously displayed cell with previously downloaded image (because of dequeueReusableCellWithReuseIdentifier:), and you will keep seeing that image until your new image will not downloaded, i.e. until execution of cell.imageView.image = image line.
So, you have to do following:
set cell.imageView.image = nil after dequeueReusableCellWithReuseIdentifier: line, like so:
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! ImageCollectionViewCell
cell.imageView.image = nil;
//...
This will remove previously downloaded image from imageView until new image download.
You should use something like SDWebImage or UIImageView+AFNetworking for async image downloading with cache support, because every time that your method is called the images will be downloaded again and again instead of getting cached image, and that is waste of traffic.
Good luck!

Adding Youtube thumbnail on UICollectionView in swift

I am trying to find a way to add a collection of youtube video thumbnails on a UICollectionView. But in the way how I am trying the thumbnail (image) is not being showed, and also I have not figure out a way to add a collection (array) of URL's on NSData.
Or if there are other ways to do so like with Youtube API.
And just to mention that I do not know Obj-C so any code in Obj-C wouldn't be very helpful.
Here is my code
var videoName:[String] = ["First Video", "Second Video", "Third Video"]
var videoImage:[String] = ["thumbnail1","thumbnail2","thumbnail3"]
let thumbnail1 = NSURL(string: "https://www.youtube.com/watch?v=sGF6bOi1NfA/0.jpg")
let thumbnail2 = NSURL(string: "https://www.youtube.com/watch?v=y71r1jhMdRk/0.jpg")
let thumbnail3 = NSURL(string: "https://www.youtube.com/watch?v=qfsRZsvraD8/0.jpg")
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell: SongCollectionView = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as! SongCollectionView
cell.labelCell.text = videoName[indexPath.row]
let imageData = NSData(contentsOfURL: /*videoImage[indexPath.row]*/ thumbnail1!) // in the commented section I am trying to add an array of strings which contains thumbnail URL's
cell.imageCell.image = UIImage(data: imageData!)
return cell
}
A solution using tuplets rather than two separate arrays
let videoData = [("First Video", "sGF6bOi1NfA/0"), ("Second Video","y71r1jhMdRk/0"), ("Third Video", "qfsRZsvraD8/0")]
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell: SongCollectionView = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as! SongCollectionView
let (name, token) = videoData[indexPath.row]
cell.labelCell.text = name
let imageData = NSData(contentsOfURL: NSURL(string: "https://www.youtube.com/watch?v=\(token).jpg")!)
cell.imageCell.image = UIImage(data: imageData)
return cell
}