SDWebImage starts to load too late - ios

In my cellForRowAtIndexPath, I am using the following function inside dequeResuableCell to update the image in the cell
let imageNSURL = NSURL(string: post.imageUrl)
let placeHolderImage = UIImage(named: "LoadingPlaceHolder")!
postImageView.sd_setImageWithURL(imageNSURL, placeholderImage: placeHolderImage, options: [], progress: { (byteSent: Int, byteExpectedToSend: Int) in
// Progress
}, completed: { (image: UIImage!, error: NSError!, imageCacheType: SDImageCacheType, url: NSURL!) in
if error != nil {
print("Error \(error.localizedDescription)")
} else {
// Image Download Complete
}
})
However, every time the cell is "just about to appear", I see a glimpse of the placeHolder image. It seems like the SDWebImage SDK only starts downloading the image close to when the cell actually appears on the screen. I was wondering if anyone else is experiencing this or knows how it could be fixed?
----Update----
In response to the comments
I thought cellForRowAtIndexPath is called way ahead of cell being actually displayed (2,3 cells?). willDisplayCell is the one that is displayed just before the cell is shown. But I guess Im wrong as what you said made sense. I was't scrolling very fast and the file is only 55kb. Caching the image works but that is only after the image gets downloaded. What I want to achieve is that the image gets downloaded maybe 2,3 cells earlier so that it is ready in the cache for the cell to be loaded.

What you are seeing is probably network latency.
Depending on the cache state of the url you are loading, SDWebImage will probably be downloading the image from a remote server and depending on the size and network type this could take a few moments.
You aren't trying to load the image until the cell is dequeued, this only happens moments before the cell is displayed. You probably want to look at something like SDWebImagePrefetcher. This would allow you to cache the images at an earlier stage in your app lifecycle and should (in most cases) ensure the images are available locally prior to showing the cell eliminating network latency.

Related

Rendering GIF images causes extensive memory usage in Swift?

When I try to render a GIF image (selected from photo library, whose data type is PHAsset), I use the following code:
PHImageManager().requestImageData(for: asset, options: nil) { (data, _, _, _) in
if let imageData = data {
imageView.image = UIImage.gif(data: imageData)
}
}
Where .gif is an extension to UIImage I copied from here, I believe many people use it.
The problem is, when I run the above code, the memory usage rises by about 20+MB, which is not outrageous, however, when I delete this selected GIF asset, the memory usage does not drop. And if I go ahead and select more GIF assets, for each one I select and run the above code, the memory usage rises by 20+MB. Now it's not acceptable anymore as the memory usage will just go up and never drop until the app crashes.
I understand why the memory usage goes up when I render a GIF image, I mean, the image data sits in the memory. What I don't know is how to I deallocate the chunk of memory when I wish to delete the GIF image?
--------------UPDATE-----------------
The UIImageView on "TestScreen" displays the thumbnail of the selected GIF image
When I press the GIF image, the app will open the image in full screen mode and if it's a GIF image, it'll play the animated image by running the above code
The memory goes up and never drops when I repeatedly open the GIF image in full screen
The memory leak may be in your own code rather than in the .gif extension. Maybe the view controller that displays the .gif does not deinitiazile when you close it. Wherever the leak is, here are two ways to find it:
a) A very simple approach is to add a print command to the de-/initialization of your objects. So you can see in the console when an object should be deinitialized and free up memory but in fact doesn't, e.g.:
class MyClass {
init() {
print("init: \(self)")
}
deinit {
print("deinit: \(self)")
}
}
b) A more insightful and convenient method is to use Xcode Instruments.
It is a much more powerful way of inspecting the memory management of your app than adding print commands. Also, once you have figured out how to use it, you will love it as it automates a lot of steps and eventually you will be able to check for memory leaks with just a few clicks.
Here you can find a tutorial on how to use Xcode Instruments.
If you post the code for the screen with the black background that opens and displays the GIF it may give a hint what the problem might be. In most cases it is something like a delegate that is not declared weak or another form of circular strong reference.

do I download the image twice if use pods to cache the image and download it asynchronously?

I am new in programming and in iOS development, I try to use kingfisher to cache and download the image asynchronously.
I have one downloadURL, and the downloaded image from that link will be used for 2 image view. for blurryImageView and for posterImageView.
the blurryImageView will be used as the background, if in case the image from the user is not in the desired vertical horizontal ratio like the picture below
my question is, if I download from that one link and will be used for 2 image view, am I actually download it twice or just once?
here is the simplified code I use :
import Kingfisher
#IBOutlet weak var posterImageView: ImageView!
#IBOutlet weak var blurryImageView: ImageView!
override func viewDidLoad() {
super.viewDidLoad()
guard let urlPoster = URL(string: imagePathString) else {return}
blurryImageView.kf.setImage(with: urlPoster)
posterImageView.kf.setImage(with: urlPoster)
}
as you can see in the code above, the image will be downloaded asynchronously, and when posterImageView.kf.setImage(with: urlPoster) is triggered, the image is ( I assume ) still not yet finished downloaded when from blurryImageView.kf.setImage(with: urlPoster) . so I am worried it will be downloaded twice which is not efficient. I want it to be downloaded only once. what should I do if I only want to download it oncee
Don't worry it won't download again. If you read the kingfisher docs it mentions that once the images are downloaded they are cached. So the next time you call the image from that url it's picked up from the cache.
Here is the line from the kingfisher docs
Kingfisher will download the image from url, send it to both the
memory cache and the disk cache, and display it in imageView. When you
use the same code later, the image will be retrieved from cache and
shown immediately.

iOS app crashes when loading an image

Goals:
Programmatically load first image (approx 94kb) into an ImageView in the main storyboard from the Assets.xcassets folder (see code below) - works perfect
Then when you load a second image (same size) into the original UIImage it causes the iOS app to crash.
Here is my code:
mainImageView.image = UIImage(named:"FirstImage.png") // load first image, no issues
Then if you programmatically load a second image into the same UIImage it causes the device to throw a low memory warning and the iOS crashes the app:
mainImageView.image = UIImage(named:"SecondImage.png") // load second image
After reading a number of answers on SO and other articles (see below), the best way to manage memory when you are loading multiple images into an animation array is to use the contentsOfFile: imageName instead of the UIImage(named:"FirstImage.png")
See Article here:
http://www.alexcurylo.com/2009/01/13/imagenamed-is-evil/
And secondly Apple states the following:
If a matching image object is not
already in the cache, this method locates and loads the image data
from disk or from an available asset catalog, and then returns the
resulting object. The system may purge cached image data at any time
to free up memory. Purging occurs only for images that are in the
cache but are not currently being used. In iOS 9 and later, this
method is thread safe. Special Considerations If you have an image
file that will only be displayed once and wish to ensure that it does
not get added to the system’s cache, you should instead create your
image using imageWithContentsOfFile:. This will keep your single-use
image out of the system image cache, potentially improving the memory
use characteristics of your app.
https://developer.apple.com/documentation/uikit/uiimage/1624146-init
Lastly, upon receiving the Memory Warning you could also create the following function:
func applicationDidReceiveMemoryWarning(application: UIApplication) {
NSURLCache.sharedURLCache().removeAllCachedResponses()
}
Hope this helps someone else :)

Updating Tableview with photos in the background

I'm currently hitting an API to get a list of inventory items. Right now I am also downloading all of the photos for all of the items on initial load. What I want to do is load the table view with all of the text (be able to interact with the tableview) and then load the photos in the background and update each row once the photo has been retrieved so that it loads faster. I have all of the URLs of the photos from the API. How would I do this?
I am using Alamofire Image for this purpose. Here is a sample code.
After installing its pod, you can do:
cell.yourImageView.af_setImageWithURL(url, placeholderImage: nil, filter: nil, imageTransition: .CrossDissolve(0.5), runImageTransitionIfCached: false, completion: {response in
// if you have activity indicator you can stop it when images is downloaded
//cell.activityIdicator.stopAnimating()
})
There are many sdk available for lazyloading. You can use SDWebImage for same.
It will automatically load image into your UIImageView as soon as image download.

PINRemoteImage delete cache

I'm using PINRemoteImage in my iOS App for setting image on UIImageView. I always have the same link for image, but in meantime image can change (I can upload different image), but whenever I call pin_setImageFromURL on UIImageView it always sets an old image (not if I delete app and reinstall it). I found out that calling [[[PINRemoteImageManager sharedImageManager] defaultImageCache] removeAllObjects] will delete image from cache but only when I close and reopen app, so does anyone known how to force app to update cache immediately after calling upper method?
I discovered this method to clear cache:
[[[PINRemoteImageManager sharedImageManager] cache] removeObjectForKey:
[[PINRemoteImageManager sharedImageManager]cacheKeyForURL:your_URL processorKey:nil]];
So, in your - (void)viewWillAppear:(BOOL)animated you can set again your ImageView with your_URL.
That did the trick at my side ;)
A simple solution in Swift for removing image from cache is:
import PINRemoteImage
import PINCache
if let cacheKey = PINRemoteImageManager.sharedImageManager().cacheKeyForURL(NSURL(string: "http://your-image-url"), processorKey: nil) {
PINRemoteImageManager.sharedImageManager().cache.removeObjectForKey(cacheKey, block: nil)
}

Resources