I am using the code given below to download the image using downloadImageWithURL method and to assign the image to a UIImageView and cache the same image using SDImageCache().storeImage, but i am not able to cache the image. Am i missing anything?
Here is my code:
SDWebImageManager.sharedManager().downloadImageWithURL(profileImageURL,
options: SDWebImageOptions.HighPriority,
progress: { (min:Int, max:Int) -> Void in
})
{ (image:UIImage!, error:NSError!, cacheType:SDImageCacheType, finished:Bool, url:NSURL!) -> Void in
if (image != nil)
{
self.userProfilePic.image = image
SDImageCache.sharedImageCache().storeImage(image, forKey: "userProfilePicImage", toDisk: true)
}
}
Looking at the github page, there's a category specifically for UIImageView. Look for Using UIImageView+WebCache category with UITableView on https://github.com/rs/SDWebImage as they give an example.
This both sets the image, caches it and uses a placeholder image whilst the image is fetching.
SDWebImage has a Method sd_setImageWithURL which will download the image and save it to Cache also, you don't need to manually save that image on Cache
Try below code it will solve your problem
self.userProfilePic.sd_setImageWithURL(NSURL(string: "http://www.domain.com/path/to/image.jpg")!, placeholderImage: UIImage(named: "placeholder.png")!)
Related
I am using AlamofireImage library to download/cache web images and show it in UIImageView inside tableViewCells.
imageView.af_setImage(withURL: url, placeholderImage: nil, filter: nil, imageTransition: .crossDissolve(0.3), runImageTransitionIfCached: true, completion: { (response) in
//...... other code .....
})
It works perfect for .png/.jpg or other still images but I am not able to show GIF images using this.
I tried using external library to convert imageData to gif images and it works perfect however Alamofire is not caching the gif data and next time the image loads as still image.
Check the code below:
imageView.af_setImage(withURL: url, placeholderImage: nil, filter: nil, imageTransition: .crossDissolve(0.3), runImageTransitionIfCached: true, completion: { (response) in
if imageUrl.hasSuffix("gif") {
if let data = response.data{
self.imageView.image = UIImage.gifImageWithData(data)
}
}
})
The above code shows the GIF for first time but next time only still image appears.
Any idea how the following can be achieved using AlamofireImage:
Download the GIF imageData for the first time, cache it and show GIF to imageView
Next time get the imageData from cache and show GIF again
I have not found a direct way to play GIF with AlamofireImage but we can do using SwiftyGif. I'm using AlamofireImage throughout the app but for display GIF and load from the server, I'm using SwiftyGif. Using SwiftyGif you can play, load from the server as well as locally. I think it will be reliable solution instead of use SDWebImage.
SwiftyGif
// You can also set it with an URL pointing to your gif
let url = URL(string: "...")
let loader = UIActivityIndicatorView(style: .white)
cell.gifImageView.setGifFromURL(url, customLoader: loader)
Download GIF file , next time get the file and show
Alamofire.download(gifUrl, to: destination).responseJSON(completionHandler: {
response in
if let filePath = response.destinationURL?.path{
if let gifImage = UIImage.init(contentsOfFile: filePath){
self.imageVIew.image = gifImage
}
}
})
I've a string in which I've comma separated links of images. Here is how I'm splitting it into an array: let imagesLinks = imageLins.components(separatedBy: ","). Then I've used for loop to get one link, download the image and storing it in a UIImage array in this way:
for imag in imagesLinks
{
let img = UIImageView()
print("\(baseReportImageURL)\(imag)")
img.sd_setImage(with: URL(string: "\(baseReportImageURL)\(imag)"), placeholderImage: nil)
imagesArray.append(img.image!)
}
The print statement is giving me the correct URL which when I open on browser downloads the image. The problem is on the line where I'm appending the array i.e. imagesArray.append(img.image!). I get:
fatal error: unexpectedly found nil while unwrapping an Optional value
and
fatal error: unexpectedly found nil while unwrapping an Optional value
So what would be the correct solution for this?
UPDATE
My question is different because I'm using SDWebImage and when I use completion block there is a strange behaviour of the app:
img.sd_setImage(with: imgURL, placeholderImage: nil,options: SDWebImageOptions(rawValue: 0), completed: { (image, error, cacheType, imageURL) in
activityIndicator.stopAnimating()
imagesArray.append(image!)
self.photoCollection.reloadData()
})
So it keeps on rotating the activity indicator and when I go back and push the view again it load the images instantly. So I think that the completion block is not called when the image is downloaded but why is that?
I think that is not the proper way to get downloaded images (set images to an UIImageView and then get the images from there).
You should use the image downloader, provided by SDWebImage:
SDWebImageManager.shared().imageDownloader?.downloadImage(with: <YOUR_URL>, options: [], progress: { (received, expected, nil) in
print(received,expected)
}, completed: { (image, data, error, true) in
yourArray.append(image)
})
If you have indexes, you can update the current row in the tableview every time when an image downloaded, or just reload() the whole, but I recommend the first.
It is because
imagesArray.append(img.image!)
Image take some times for downloading. When you do imagesArray.append(img.image!) at that time it is possible that your image is not set to your imageview and that means you are trying to add nil in your imageArray!
Second thing why are you storing image in an array ? You have array of urls and you are using SDWebImage then every time when you want to display image use SDWebImage. No need to store in array!
And if you want images in array anyhow than use NSUrlSession asynchronous requests with completion handlers and from completion handler add image to your array!
Append the image into the images array in the sd_setImage completion block.
The image is not yet downloaded when you are trying to add it into the array, and check if the image is not equal nil before adding it.
Use this Block For Image Download After download Perform Operation(append Image)
cell.appIcon.sd_setImage(with: url!, placeholderImage: UIImage(named: "App-Default"),options: SDWebImageOptions(rawValue: 0), completed: { (image, error, cacheType, imageURL) in
// Perform operation.
//append Image Here
})
I'm using SDWebImage library to cache web images in my UICollectionView:
cell.packItemImage.sd_setImage(with: URL(string: smileImageUrl[indexPath.row]))
but I want to save the cached images locally in a file instead of downloading them again
FileManager.default.createFile(atPath: newPath, contents: Data(contentsOf: URL(string: snapchildvalue[Constants.smiles.smileImageUrl] as! String)!), attributes: nil)
is there a way to get the data of cached images
SDWebImage caches downloaded images automatically by default. You can use SDImageCache to retrieve images from the cache. There is a memory cache for the current app session, which will be quicker, and there is the disk cache. Example usage:
if let image = SDImageCache.shared().imageFromDiskCache(forKey: imageURL.absoluteString) {
//use image
}
if let image = SDImageCache.shared().imageFromMemoryCache(forKey: imageURL.absoluteString) {
//use image
}
Also make sure you import SDWebImage in your file. (If you're using Swift/Carthage, it will be import WebImage
SDWebimage chaches image once it is downloaded from a url. Basically it saves image against a url and next time if an image is available for a URL. It will simply get that image from cache. So the below method will be called instantly if the image is already downloaded to device.
imgView.sd_setImage(with: URL(string:url), completed: { (image, error, type, url) in
imgView.image = image
//Do any thing with image here. This will be called instantly after image is downloaded to cache. E.g. if you want to save image (Which is not required for a simple image fetch,
//you can use FileManager.default.createFile(atPath: newPath, contents: UIImagePNGRepresentation(image), attributes: nil)
})
Still if you want to save that image somewhere else or modify it or whatever, you can do it in the completion block above.
SDWebImage already have this kind of caching file locally
Create a SDImageCache with namespace of your choice
Try get the image with imageCache.queryDiskCache
If the image exist, set it to your imageview, if not, use sd_setImage to get the image then save it to the local cache with SDImageCache.shared().store
The key usually to be the image url string
Something like this, might not be correct syntax:
imageCache.queryDiskCache(forKey: urlForImageString().absoluteString, done: {(_ image: UIImage, _ cacheType: SDImageCacheType) -> Void in
if image {
self.imageView.image = image
}
else {
self.imageView.sd_setImage(withURL: urlForImageString(), placeholderImage: UIImage(named: "placeholder")!, completed: {(_ image: UIImage, _ error: Error, _ cacheType: SDImageCacheType, _ imageURL: URL) -> Void in
SDImageCache.shared().store(image, forKey: urlForImageString().absoluteString)
})
}
})
I'm trying to download an image called "aaa.jpeg" in my s3 bucket using AWSMobileHubHelper. I found this function on their documentation site.
func downloadContent(content: AWSContent, pinOnCompletion: Bool) {
content.downloadWithDownloadType( .Always, pinOnCompletion: pinOnCompletion, progressBlock: {(content: AWSContent?, progress: NSProgress?) -> Void in
// Handle progress feedback
}, completionHandler: {(content: AWSContent?, data: NSData?, error: NSError?) -> Void in
if let error = error {
print("Failed to download a content from a server.)")
// Handle error here
return
}
// Handle successful download here
if let image = UIImage(data: data!){
self.imageView = image
}
})
}
Once the download is successful (it Did, There is no error message), I'm trying to assign the image to an imageView.
I can tell that the data has been downloaded successfully. I can print the data and see a familiar binary structure of an image. But for some reasons I can't assign the UIImage to the imageView. Because I can't convert the data to a UIImage.
I just want to know if this is the proper way to download image from s3 or am I missing something. Does "data" in the completion block carry the downloaded image? I can't seem to find any documentations on this.
Is this the correct function to use to download from S3?
Yes, the data contains the actual image data. You can put the downloaded data in an UIImageViewController and it should open up fine. Also, this is demonstrated in a Sample App which can be downloaded from the Mobile Hub Console.
OverView
I am downloading the images from web server using SDWebImage in my collectionView cell.
if floor.hasTitleImage != nil {
self.floorImageView.sd_setImageWithURL(NSURL(string:(floor.hasTitleImage?.imageURL)!), placeholderImage: UIImage(named: "Placeholder"), completed: { (imageDownloaded, error, cacheType, url) in
if imageDownloaded.size.width > 300 {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
let resizedImage = imageDownloaded.resizePreservingAspectRatio(toSize: CGSize(width: 300, height: 300))
SDImageCache.sharedImageCache().removeImageForKey(NSURL(string:(floor.hasTitleImage?.imageURL)!)?.absoluteString, fromDisk: true, withCompletion: {
SDImageCache.sharedImageCache().storeImage(resizedImage, forKey: NSURL(string:(floor.hasTitleImage?.imageURL)!)?.absoluteString, toDisk: true)
})
})
}
})
}
else{
self.floorImageView.image = UIImage(named: "Placeholder")
}
Now what this code is doing is downloading the image using the url stored in core data element called floor. Now the images downloaded are way bigger then my ImageView so am resizing it to somewhere around 300px.
I know that SDWebImage does intense caching by default and uses url's absolute string as key for its cache. So Once resized! I replace the original image with my resized image.
Issue
Everything worked fine as expected! till I tested it with low internet speed which caused the image download to be delayed.
If the image downloading takes time, till then if I scroll the tableView and the cell gets reused, once the image downloaded SDWebImage loads the imageView of the cell but ends up resulting the wrong cell with wrong image.
Though this happens only once as for next time onwards image will be loaded from cache everything works fine. But Is this the expected behaviour or am I doing something wrong???
Solutions I could think of :
As sd_setImageWithURL by default sets the value of imageView with downloaded image rather than downloading the image using sd_setImageWithURL, if I can download it using,
if floor.hasTitleImage != nil {
if let downloadedImage = SDImageCache.sharedImageCache().imageFromMemoryCacheForKey(NSURL(string:(floor.hasTitleImage?.imageURL)!)?.absoluteString){
self.floorImageView.image = downloadedImage
}
else if let diskImage = SDImageCache.sharedImageCache().imageFromDiskCacheForKey(NSURL(string:(floor.hasTitleImage?.imageURL)!)?.absoluteString){
self.floorImageView.image = diskImage
}
else{
let downloadManager = SDWebImageManager.sharedManager();
downloadManager.downloadImageWithURL(NSURL(string:(floor.hasTitleImage?.imageURL)!), options: [], progress: nil, completed: { (downloadedImage, error, cacheType, finished, url) in
if downloadedImage.size.width > 300 {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
let resizedImage = downloadedImage.resizePreservingAspectRatio(toSize: CGSize(width: 300, height: 300))
SDImageCache.sharedImageCache().removeImageForKey(NSURL(string:(floor.hasTitleImage?.imageURL)!)?.absoluteString, fromDisk: true, withCompletion: {
SDImageCache.sharedImageCache().storeImage(resizedImage, forKey: NSURL(string:(floor.hasTitleImage?.imageURL)!)?.absoluteString, toDisk: true)
})
if floor.hasTitleImage?.imageURL != nil && floor.hasTitleImage?.imageURL! == url{
dispatch_async(dispatch_get_main_queue(), {
self.floorImageView.image = SDImageCache.sharedImageCache().imageFromMemoryCacheForKey(NSURL(string:(floor.hasTitleImage?.imageURL)!)?.absoluteString)
self.reloadInputViews()
})
}
})
}
})
}
}
else{
self.floorImageView.image = UIImage(named: "Placeholder")
}
Though it works it will load the image only when I scroll the tableView :(
Questions :
Is what sd_setImageWithURL doing is expected behaviour ?? I mean every single one of the app using SDWebImage with tableView or CollectionView must have faced this correct ?? So if no body faced it I must be doing something wrong :( Am I doing anything wrong ?
Is what am doing using SDWebImageManager.sharedManager is correct ??? Is that long code required? Checking memory cache then disk cache and finally making call to download image isn't it too much :o
Why is that my second approach not loading the image once downloaded and expects the collection view to scroll and reload the cell to display the image ??
EDIT
I solved the 3rd question :) Sorry my mistake. I was comparing if floor.hasTitleImage?.imageURL != nil && floor.hasTitleImage?.imageURL! == url{ which is wrong because url above is NSURL where as floor.hasTitleImage?.imageURL! is NSString.
So Updated solution :
if floor.hasTitleImage?.imageURL != nil && floor.hasTitleImage!.imageURL! == url.absoluteString{
dispatch_async(dispatch_get_main_queue(), {
self.floorImageView.image = SDImageCache.sharedImageCache().imageFromDiskCacheForKey(NSURL(string:(floor.hasTitleImage?.imageURL)!)?.absoluteString)
self.setNeedsLayout()
})
}
Though my second approach downloads the image now and updates the cell properly but now image downloading is very slow compared to sd_setImageWithURL.
Is there any way I can use the method1 sd_setImageWithURL and get the image updated properly?
Please help me!! what am doing wrong !! Thanks in advance :)
Is what sd_setImageWithURL doing is expected behaviour ?? I mean every single one of the app using SDWebImage with tableView or CollectionView must have faced this correct ?? So if no body faced it I must be doing something wrong :( Am I doing anything wrong ?
No, it's not expected behaviour. I'm using SDWEbImage as well both tableView and collectionView and both are work fine.
Is what am doing using SDWebImageManager.sharedManager is correct ??? Is that long code required? Checking memory cache then disk cache and finally making call to download image isn't it too much :o
You don't have to cache image youself, SDWebImage take care caching process for you (in memory and disk).
If you need to resize image before cache and display, I think better do it by implement SDWebImageManagerDelegate which allows you to transform the image after it has been downloaded but before cache and display.
UPDATE:
If you use sd_setImageWithURL, there is another solution to prevent wrong assign image is by override prepareForReuse() method in UICollectionViewCell subclass and call sd_cancelCurrentImageLoad() on imageView and set image to nil.
hope this help.