Using SdWebImage and Image Change immediately by swift - ios

This is Question Video
I have a problem about imageView by using SDWebImage.
I change user's image and already get new user's image url, but when I push to this ViewController, it will show the old image first and change to new image.
What's wrong with me?
Thanks.
var avatar:String = "" // previous VC data pass to here
var photoImageView:UIImageView = { () -> UIImageView in
let ui = GeneratorImageView()
ui.backgroundColor = UIColor.clear
ui.layer.masksToBounds = true
ui.contentMode = .scaleAspectFill
return ui
}()
override func viewDidLoad() {
super.viewDidLoad()
iconImageFromUrl(imageView: iconImageView, url: avatar, isResize: false)
}
func iconImageFromUrl(imageView:UIImageView, url:String,isResize:Bool) {
imageView.setShowActivityIndicator(true)
imageView.setIndicatorStyle(.gray)
imageView.sd_setImage(with: URL(string:url), placeholderImage: nil, options: .lowPriority, progress: nil
, completed: { (image, error, cacheType, url) in
guard image != nil else{
imageView.image = resizeImage(image: #imageLiteral(resourceName: "defaultIcon"), newWidth: 50)
return
}
DispatchQueue.global().async {
let data = try? Data(contentsOf: url!) //make sure your image in this url does exist, otherwise unwrap in a if let check / try-catch
if data != nil
{
if let image = UIImage(data: data!)
{
DispatchQueue.main.async {
if isResize == true{
imageView.image = resizeImage(image: image, newWidth: 250)
}else{
imageView.image = image
}
}
}
}
}
})
}

sd_setImage method is written inside a category of UIImageView. After downloading the image it sets the image on UIImageview on its own and in the completion closure returns the downloaded/cached UIImage as well.
You dont need to create Data from imageUrl and set it again. If you want to resize image, you can do it on the returned image.
Also, you dont need to check the image nil for setting the default image, just pass the resized default image as placeholder image
imageView.sd_setImage(with: URL(string:url), placeholderImage: resizeImage(image: #imageLiteral(resourceName: "defaultIcon"), newWidth: 50), options: .lowPriority, progress: nil
, completed: { (image, error, cacheType, url) in
guard image != nil else {
return
}
if isResize {
imageView.image = resizeImage(image: image, newWidth: 250)
} })

Related

Nil while caching images

I've been able to solve the issue of caching images to improve scroll performance in my app. However nil is found when it tries to add it to cache. Also how can I add a placeholder image for images that failed to load or aren't available ?
let imageCache = NSCache<NSString, UIImage>()
extension UIImageView {
func downloadImage(from imgURL: String) -> URLSessionDataTask? {
guard let url = URL(string: imgURL) else { return nil }
// set initial image to nil so it doesn't use the image from a reused cell
image = nil
// check if the image is already in the cache
if let imageToCache = imageCache.object(forKey: imgURL as NSString) {
self.image = imageToCache
return nil
}
// download the image asynchronously
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if let err = error {
print(err)
return
}
DispatchQueue.main.async {
// create UIImage
let imageToCache = UIImage(data: data!)
// add image to cache
imageCache.setObject(imageToCache!, forKey: imgURL as NSString)
self.image = imageToCache
}
}
task.resume()
return task
}
}
A couple of observations:
Just supply placeholder as parameter to function and use it instead of nil to initialize the image.
Do that after checking the cache (because there’s no point in using the placeholder if you found desired image in the cache).
Avoid use of ! forced unwrapping operator.
Check that UIImage(data:) found an image in the guard statement (and on the session queue, not the main thread).
Thus:
let imageCache = NSCache<NSString, UIImage>()
extension UIImageView {
func downloadImage(from imgURL: String, placeholder: UIImage? = nil) -> URLSessionDataTask? {
guard let url = URL(string: imgURL) else { return nil }
// check if the image is already in the cache
if let imageToCache = imageCache.object(forKey: imgURL as NSString) {
image = imageToCache
return nil
}
// set initial image to placeholder so it doesn't use the image from a reused cell
image = placeholder
// download the image asynchronously
let task = URLSession.shared.dataTask(with: url) { data, _, error in
guard
let data = data,
error == nil,
let imageToCache = UIImage(data: data)
else {
print(error ?? URLError(.badServerResponse))
return
}
imageCache.setObject(imageToCache, forKey: imgURL as NSString)
DispatchQueue.main.async {
self.image = imageToCache
}
}
task.resume()
return task
}
}

How i can see the image inside UIImageView?

I have an array filled with parsed json data including image url's. But when i try to see that images inside uiimageview, it doesn't the show. What should i do
I printed the url. This is my url inside array.
This is my array
var feedResult = [Result]()
It shows the name inside collectionview but i couldn't see the images. I used named like everybody does. But what is missing?
let info = feedResult[indexPath.row]
cell.appLabel.text = info.artistName
cell.customCollectionImage.image = UIImage(named: info.artWorkUrl)
You have to download the image Data using the url you got, only then you will use the downloaded data, like so:
imageView.image = UIImage(data: downloadedData)
Here is a quick subclass of UIImageView that does the downloading:
class URLImageView: UIImageView {
func download(url urlString: String) {
guard let url = URL(string: urlString) else { return }
let task = URLSession.shared.dataTask(with: url) { (downloadedData, _, error) in
guard error == nil && downloadedData != nil else { return }
DispatchQueue.main.async{
self.image = UIImage(data: downloadedData!)
}
}
task.resume()
}
}
Update-1 Use the download function using UIImageView extension, without subclassing, like so:
extension UIImageView {
func download(url urlString: String) {
guard let url = URL(string: urlString) else { return }
let task = URLSession.shared.dataTask(with: url) { (downloadedData, _, error) in
guard error == nil && downloadedData != nil else { return }
DispatchQueue.main.async{
self.image = UIImage(data: downloadedData!)
}
}
task.resume()
}
}
Usage:
cell.customCollectionImage.download(url: info.artWorkUrl)
By using UIImage(named: info.artWorkUrl) you are not accessing the image in your array but the images in your Assets.xcassets (assets that you add manually in your project).
You need to download the image from the artWorkUrl and then directly use the downloaded image like this:
cell.customCollectionImage.image = UIImage(data: yourImageData)
Where yourImageData is what you have downloaded from the server with the artWorkUrl.

Image View Does not display image after setting image to it downloaded from url

here it is what i am doing.
extension UIImageView {
func downloadedFrom(url: URL, contentMode mode: UIViewContentMode = .scaleAspectFit) {
contentMode = mode
URLSession.shared.dataTask(with: url) { data, response, error in
guard
let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200,
let mimeType = response?.mimeType, mimeType.hasPrefix("image"),
let data = data, error == nil,
let image = UIImage(data: data)
else { return }
DispatchQueue.main.async() {
self.image = image
}
}.resume()
}
func downloadedFrom(link: String, contentMode mode: UIViewContentMode = .scaleAspectFit) {
guard let url = URL(string: link) else { return }
downloadedFrom(url: url, contentMode: mode)
}
i checked it in image its show image in preview in debug state. but after self.image = image
nothing change on image view image is not displaying. anyone know where the problem is ? thanks
Most efficient way for downloading image: SDWebImage
You can use a Cocoa Pod file for SDWebImage Library.
init the pod file.
pod 'SDWebImage'
install Pod file.
Swift snippet:
import SDWebImage
imageView.sd_setImage(with: URL(string: #"user-url"), placeholderImage: UIImage(named: "placeholder"))
Objective-C Snippet:
#import <SDWebImage/UIImageView+WebCache.h>
...
[imageView sd_setImageWithURL:[NSURL URLWithString:#"user-url"]
placeholderImage:[UIImage imageNamed:#"placeholder"]];
Hope this will help to do efficient way to downloading images basically, whenever you are using UICollectionView or UITableView.
You can do it easier with Kingfisher library like below.
let url = URL(string: "url_of_your_image")
imageView.kf.setImage(with: url)
Try this below code
if data != nil { // check nil here
let image = UIImage(data: data)
DispatchQueue.main.async {
self.image = image
}
}
}.resume()
no need to check the image mime type, just a check of data and image is sufficient, since data will be nil for error and vice versa.
change as
URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, let image = UIImage(data: data) else { return }
DispatchQueue.main.async {
self.image = image
}
}.resume()
And cross check with image with debugger.

Fetch Thumbnail from Video URL using SDWebImage

I need to fetch thumbnail from a video URL to display in ImageView that is in a UITableViewCell.
I can get thumbnail using this method but it takes lots of resource and hence the tableview scrolling lags and stops for the time cellForRowAtIndexPath is called.
This is the method.
func createThumbnailOfVideoFromFileURL(videoURL: String) -> UIImage? {
let asset = AVAsset(url: URL(string: videoURL)!)
let assetImgGenerate = AVAssetImageGenerator(asset: asset)
assetImgGenerate.appliesPreferredTrackTransform = true
let time = CMTimeMakeWithSeconds(Float64(1), 100)
do {
let img = try assetImgGenerate.copyCGImage(at: time, actualTime: nil)
let thumbnail = UIImage(cgImage: img)
return thumbnail
} catch {
return UIImage(named: "ico_placeholder")
}
}
Can anyone please suggest me an alternative to use SDWebImage so that the images go to the cache and are not fetched everytime UITableView delegates are called.
Kindly suggest any other approach if i m wrong.
I used the above method as it is and cached the images in an array of UIImage and made a check in cellForRowAtIndexPath that if image exists at that particular index then use that else fetch the image in background.
Not using SDWebImage but this code works like a charm
func getThumbnailFromUrl(_ url: String?, _ completion: #escaping ((_ image: UIImage?)->Void)) {
guard let url = URL(string: (url ?? "")) else { return }
DispatchQueue.main.async {
let asset = AVAsset(url: url)
let assetImgGenerate = AVAssetImageGenerator(asset: asset)
assetImgGenerate.appliesPreferredTrackTransform = true
let time = CMTimeMake(value: 2, timescale: 1)
do {
let img = try assetImgGenerate.copyCGImage(at: time, actualTime: nil)
let thumbnail = UIImage(cgImage: img)
completion(thumbnail)
} catch let error{
print("Error :: ", error)
completion(nil)
}
}
}
Usage
self.getThumbnailFromUrl(video.url.description) { (image) in
//Use image where you want to use
}

Async image loading from url inside a UITableView cell - wrong image loading while scrolling

I have a tableView that displays an image in the cell. Most of the time the correct image will be displayed, however occasionally it will display the wrong image (usually if scrolling down the tableView very quickly). I download the images asynchronously.
cell.profImg.getImgFromUrl(link: man.img, contentMode: cell.profImg.contentMode)
And here i do async request:
extension UIImageView {
func getImgFromUrl(link link:String, contentMode mode: UIViewContentMode) {
guard
let url = NSURL(string: link)
else {return}
contentMode = mode
NSURLSession.sharedSession().dataTaskWithURL(url, completionHandler: { (data, response, error) -> Void in
guard
let httpURLResponse = response as? NSHTTPURLResponse where httpURLResponse.statusCode == 200,
let mimeType = response?.MIMEType where mimeType.hasPrefix("image"),
let data = data where error == nil,
let image = UIImage(data: data)
else { return }
dispatch_async(dispatch_get_main_queue()) { () -> Void in
self.image = image
}
}).resume()
}
}
I think it's because you do not reset the content of your UIImageView when you start loading you HTTP image. So, when the cell is reused, you display the previously loaded image.
You just have to start your getImgFromUrl by something like self.image = nil (if you want a blank image) or self.image = myPlaceholderImage (if you want a placeholder image during the loading time). Here is how to integrate it in your code:
extension UIImageView {
func getImgFromUrl(link: String, contentMode mode: UIView.ContentMode) {
guard let url = URL(string: link) else { return }
contentMode = mode
URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) -> Void in
guard
let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200,
let mimeType = response?.mimeType, mimeType.hasPrefix("image"),
let data = data, error == nil, let image = UIImage(data: data) else { return }
DispatchQueue.main.async {
self.image = image
}
}).resume()
}
}
But I think you should consider the use of SDWebImage. This library provides a category for UIImageView with support for remote images coming from the web. It will be much more efficient and easier for you.

Resources