So before anything else, I found this:
SDWebImage process images before caching
which kind of helps but I really want to use SDWebImage, is there a way to process the images via a method outside of the class before completion?
To my understanding, the completion block in
- (void)setImageWithURL:(NSURL *)url completed:(SDWebImageCompletedBlock)completedBlock
is run after the image is placed. I would like to run a method before the loaded image is placed.
Is there an easy way to do this?
thank you,
reposting my answer from: SDWebImage process images before caching
SDWebImage developer Olivier Poitrey answered this question for me here.
You have to implement the SDWebImageManagerDelegate protocol and then set it as the shared manager's delegate like this:
SDWebImageManager.sharedManager.delegate = self;
using the imageManager:transformDownloadedImage:withURL: instance method.
More information.
Worked perfectly for me.
Two options:
Write your own routine that would Use Asynchronous Image Caching Independently, process the image, and then set the image view's image property. There are interesting permutations you'd have to worry about (e.g. if cell has been reused before the download has finished, you'd want to cancel the prior image download for that image view), so you need to be careful, but it's not too bad.
Probably easier, though, would be to write your own UIImageView category that is like the existing one for SDWebImage (UIImageView+WebCache.m), but introduce your own permutation of setImageWithURL which is just like the original, but offers a pre-process completion block, too.
I'd probably lean towards the latter, but both would work perfectly well
Related
I've created a small photo gallery which is presenting a new view controller with a larger version of the photo and some additional text when it is clicked:
The problem is - after going through a handful of images - the application crashes due to overuse of memory. I attempted to resolve this by compressing the images in order to leave a smaller memory footprint, but the issue remains and I'm not sure what else I can do to resolve this issue.
Also - there is almost no code to doing this since I'm using storyboard's push segues as well as the built in navigation item to go back between viewControllers.
P.S.
If you feel source code is necessary to provide insight in this instance - it can be found here:
https://www.dropbox.com/s/q1qq8pq4tzv8wyo/EXAMPLE%20BUILD.zip?dl=0
To resolve this issue you have to use this trick; Put a "placeHolder" image in your cell's imageview in "StoryBoard". Don't load the images all at once in your "ViewController", load them one by one by running a loop or in your "cellForRowAtIndexPath()" method and add a delay in each iteration (Load first image then add a delay, load second image and add a delay, then for third one and so on up to the last image).
If you want to know how to add a delay then check this link:
NSTimer - how to delay in Swift
To resolve this issue I simply resized the images - I noticed I accidentally used a gigantic (6000 x 4000) image and even though I compressed the images iOS had to crunch pretty hard to resize them into the view... thereby causing the memory leak and subsequent crash.
Resizing them to 600x400 did the trick.
So, I have this news feed structure. Every news block is a custom UITableViewCell. Now, every custom cell has a CollectionView that shows images.
The problem is that when scrolling, and news cell (news block) comes out visible, the CollectionView is reloading - every time cell shows up. I'm trying to find the way to cache those images on the main ViewController side.
What would be the best approach?
If you want to avoid implementing your own cache/disk/memory handlers for this kind of job I strongly recommend either AFNetworking or SDWebImage that handle it all for you.
An example of SDWebImage on how to set an image and its cache:
[imageView sd_setImageWithURL:[NSURL URLWithString:#"http://www.domain.com/path/to/image.jpg"]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
This handles all the cache/disk/memory for you automatically.
Here is example of how you handle cache with SDWebImage:
// Set memory size limit (check with older devices to avoid memory errors when setting it to high value.)
[[SDImageCache sharedImageCache] setMaxMemoryCost:****];
// Clear disk cache
[[SDImageCache sharedImageCache] clearDiskOnCompletion:^{
}];
// Clear memory cache
[[SDImageCache sharedImageCache] clearMemory];
I would stay away from NSUserDefaults or the above mentioned example setting the images to dictionary or arrays to avoid memory errors and app crashes/performance.
You have to cache your UIImage instance, so, if you get some pictures from any source, you might write something like below:
/* some code before */
UIImage *yourImage = /* Get your UIImage from any available way */
[self.cache setObject:yourImage forKey:#"SomeUniqueID"];
/* some code after */
I guess, this code will look greater as a function, which obtains UIImage instance and it's id as parameters.
For my opinion, it's the most common way to store images. If you need a more proficient way without any overheads with third-party frameworks, you can read the documentation about NSCache.
But in general, you have a lot of different ways how to store your images:
Get the image from the disk
Get the image from the memory
Use one of the variants above, but apart them still use some lightweight & flexible frameworks, you can find a lot of on the GitHub.
So, the code I wrote above will just store images into the memory cache.
I am using SDWebImage library for handling image cache. I implemented one of its method to download image from server and populating data in tableView. Here is my code for downloading image and showing in tableViewCell
In cellForRowAtIndexPath, I did the following code
[cell.profileImageView sd_setImageWithURL:[NSURL URLWithString:url] placeholderImage:nil options:SDWebImageRefreshCached];
It works perfectly here. But when I updated image on server, the tableViewCell still shows the same image. The problem is ,its cache is not updating image. I searched with the same keywords and came across this question on StackOverflow. But couldn't resolve the issue. I also tried to clear memory and cache on viewDidDisappear
-(void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:YES];
[[SDImageCache sharedImageCache]clearMemory];
[[SDImageCache sharedImageCache]clearDisk];
}
But its not efficient way. Is there any other way to update the cache when image on server is updated ?
You can use SDWebImageRefreshCached option to refresh concrete image, however you never know if the image was changed on the server side. So it's only your decision should you use image cache or not and when your cache should be updated.
From SDWebImage docs :
If you don't control the image server you're using, you may not be able to change the URL when its content is updated. In such case, you may use the SDWebImageRefreshCached flag. This will slightly degrade the performance but will respect the HTTP caching control headers
[imageView sd_setImageWithURL:url
placeholderImage:[UIImage imageNamed:#"placeholder.png"]
options:SDWebImageRefreshCached];
It would defeat the purpose of Image Caching if you use this in all your code. Coz the image will be downloaded everytime. Instead you could :
If you manage the server, you could setup some flags in the DB to indicate the image has changed on the server.
Implement some logic to only use SDWebImageRefreshCached once in 2 days or something like that, and use the normal code all other times.
You could also clear the entire cache once in a while to force a refresh, if that's feasible.
I am currently successfully using 'SDWebImageManager downloadImageWithURL' for downloading single images , followed by the delegate method 'transformDownloadedImage' automatically called upon completion to resize the images before caching them.
I would like, however, in the background to prefetch a bunch of images (~25) not yet displayed using the prefetcher code below in a similar way. However the problem is that the 'transformDownloadedImage' delegate is not called upon completion (of 1 or all the images) - images are cached as is.
SDWebImagePrefetcher *prefetcher = [SDWebImagePrefetcher sharedImagePrefetcher];
[prefetcher prefetchURLs:array progress:nil completed:^(NSUInteger completedNo, NSUInteger skippedNo) {
}];
Am I missing something? or is there some other efficient way to do this by pulling out the cached images upon completion, resizing, and reinserting? I am using "UIImage+Resize" to resize and manipulate, and obviously this needs to happen in background without blocking the UI.
Any and all advice about how to go about this efficiently will be greatly appreciated!
If you look at the implementation of SDWebImagePrefetcher you'll notice that it's using SDWebImageManager to perform the downloads, and it's available as a property. So you should be able to do something like this:
prefetcher.manager.delegate = self;
Now you can implement the downloadImageWithURL delegate method as you did before. I didn't try it but it should work.
I'm setting the FBProfilePictureView with the user id but it takes a while, so i'd like to know if there is anything that indicates when the loading is done, so i can show an ActivityIndicator
You should take a look at SDWebImage librabry, you will be able to put a placeholder while loading your picture or add a progress indicator. I think everything you need is there.
Hope it helps.
There is no delegate or something similar in FBProfilePictureView that indicates when the image finished loading. Looking at the source code (https://github.com/facebook/facebook-ios-sdk/blob/master/src/UI/FBProfilePictureView.m) it seems like it should be fairly easy to fork the repo and add a few changes to notify you when the HTTP request has finished.