I am working on an app which is image downloading. Images are of different sizes. I want to download image async and update table view when image downloading. Also need to clear cache on a time interval.
Please suggest me how to implement. I have seen SDwebimagecache but it is crashing on image downloading.
I hope it's can help you. I use SDWebImage with my all (ARC)projects.
using :
add your viewcontroller : #import "UIImageView+WebCache.h"
[yourImageView setImageWithURL:[NSURL URLWithString:#"ImageUrl"]];
you need to add MapKit and ImageIO to the project. if you didn't add
To do that:
Click on the project at the top of the project navigator in Xcode
Select the 'Build Phases' tab.
Open up the 'Link Binary with Libraries' box.
Click the '+'.
Add MapKit and ImageIO frameworks.
I have used the core data and AFNetworking to achieve the same thing, find below my code
UserBasicInfo* userBasicInfo = [[UserBasicInfo findByAttribute:#"userId" withValue:#(chatUser)] objectAtIndex:0];;
if (userBasicInfo.userImage == nil) {
__weak LGMessageBoxCell *weakCell = cell;
[cell.userImage setImageWithURLRequest:[[NSURLRequest alloc] initWithURL:[NSURL URLWithString:userBasicInfo.imageUrl]]
placeholderImage:[UIImage imageNamed:#"facebook-no-user.png"]
success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image){
weakCell.userImage.image = image;
[weakCell setNeedsLayout];
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
UserBasicInfo* userBasicInfo = [[UserBasicInfo findByAttribute:#"userId" withValue:#(chatUser) inContext:localContext] objectAtIndex:0];
userBasicInfo.userImage = UIImagePNGRepresentation(image);
} completion:^(BOOL success, NSError *error) {
NSLog(#"%#",[error localizedDescription]);
}];
}
failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error){
}];
} else {
cell.userImage.image = [UIImage imageWithData:userBasicInfo.userImage];
}
Related
UIImageview + afnetworking downloads images and caches the images.
But in certain cases the server images are = 15mb. So i need to compress them based on the some factor and make it to 1mb and then require to cache them.
SDWebImageCache on the other hand make you to define your own cache and store them
Is there any build in mechanism for downloading,editing and then later saving into the cache?
[SDWebImageDownloader.sharedDownloader downloadImageWithURL:imageURL
options:0
progress:^(NSInteger receivedSize, NSInteger expectedSize)
{
// progression tracking code
}
completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished)
{
if (image && finished)
{
// do something with image
}
}];
then use
[[SDImageCache sharedImageCache] storeImage:myImage forKey:myCacheKey]
Is there any other alternative to doing something like this?
Your scenario with SDWebImage is correct.
For editing purpose you need set delegate to SDWebImageManager object and implement necessary method:
// Set delegate
[SDWebImageManager sharedManager].delegate = self;
// Implement delegate method
- (UIImage *)imageManager:(SDWebImageManager *)imageManager
transformDownloadedImage:(UIImage *)image
withURL:(NSURL *)imageURL {
UIImage scaledImage = ... // Make scale based on 'image' object
return scaledImage;
}
Note that this method called immediately after image was downloaded but before storing it to memory cache and before completion block is called.
Documentation for this method:
Allows to transform the image immediately after it has been downloaded
and just before to cache it on disk and memory. NOTE: This method is
called from a global queue in order to not to block the main thread.
After that you will be able to use SDWebImageDownloader and SDImageCache as in your question:
[SDWebImageDownloader.sharedDownloader downloadImageWithURL:imageURL
options:0
progress:^(NSInteger receivedSize, NSInteger expectedSize) {
// progression tracking code
}
completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {
if (image && finished) {
[[SDImageCache sharedImageCache] storeImage:image forKey:myCacheKey];
}
}];
Then you can manage cache by using methods of SDImageCache class:
- (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(SDWebImageQueryCompletedBlock)doneBlock;
- (void)removeImageForKey:(NSString *)key fromDisk:(BOOL)fromDisk withCompletion:(SDWebImageNoParamsBlock)completion;
If you need algorithm for image scaling by max data size take a look on this answer.
I'm trying to download an image file using Quickblox API. I can upload a file but when I try to download, NSData image doesn't show an image.
// Upload a user avatar, previously log in Quickblox API
NSData *avatar = UIImagePNGRepresentation([UIImage imageNamed:#"userWhite"]);
[QBRequest TUploadFile:avatar fileName:#"avatar.png" contentType:#"image/png" isPublic:YES successBlock:^(QBResponse *response, QBCBlob *blob) {
// Success
} statusBlock:^(QBRequest *request, QBRequestStatus *status) {
} errorBlock:^(QBResponse *response) {
}];
Image Upload: Image
Download avatar:
NSString* userProfilePictureID = [NSString stringWithFormat:#"%ld",(long)[[LocalStorageService shared] currentUser].blobID]; // user - an instance of QBUUser class
// download user profile picture
[QBRequest downloadFileWithUID:#"318547" successBlock:^(QBResponse *response, NSData *fileData) { UIImage* image = [UIImage imageWithData:fileData];
} statusBlock:^(QBRequest *request, QBRequestStatus *status) {
} errorBlock:^(QBResponse *response) {
}];
UIImage doesn't show any image. What can I do? NSData isn't corrupted.
Your download code looks right. You basically should call QBRequest method and pass blob.UID to it.
But in my case blob.UID is something like this 9357c3d66b944880a82cdbeb836f143c00. However there are steps I took to accomplish your task:
1) Sign up or login user
Either by calling +[QBRequest signUp:successBlock:errorBlock:] or +[QBRequest logInWithUserLogin:password:successBlock:errorBlock].
2) Enumerate through all available blobs for current user
[QBRequest blobsWithSuccessBlock:^(QBResponse* response,
QBGeneralResponsePage* page,
NSArray* blobs) {
[self findAndShowAvatarFromBlobs:blobs];
} errorBlock:^(QBResponse* response) { <...> }];
3) Find desired blob and download it
- (void)findAndShowAvatarFromBlobs:(NSArray*)blobs {
for (QBCBlob* blob in blobs) {
if ([blob.name hasPrefix:#"avatar"]) {
[QBRequest downloadFileWithUID:blob.UID
successBlock:^(QBResponse* response, NSData* fileData) {
UIImage* image = [UIImage imageWithData:fileData];
self.imageView.image = image;
}
statusBlock:^(QBRequest* request, QBRequestStatus* status) { <...> }
errorBlock:^(QBResponse* response) { <...> }];
}
}
}
Probably you have a problem with getting valid blob UID (I suspect that you are using blob ID which is not the same as UID). Is it possible for you to use the same kind of logic?
Full project you can find here: https://dl.dropboxusercontent.com/u/930742/so/QuickBloxSample.zip
It creates necessary user, uploads and then downloads image with showing it in UIImageView demonstrating all described steps. (Don't forget to set your service and application keys in AppDelegate)
P.S. In admin panel you can find blob UID by clicking on its ID in Content table
I'm wondering what is the best way to set images in my UITableView from the server. I'm storing images on the amazon s3 and to get images I have implemented logic in the backend that require two statements:
first one I'm using to get url of image and in the second one I just simply display images to the UITableViewCell. For web requests I'm using AFNetworking library and addition to UIImageView (<UIImageView+AFNetworking.h>). I don't have a problem with requests, however I'm not quite sure where should I put my code responsible for getting all urls of the images. It seems to me that cellForARowAtIndexPath method is good only for displaying images from the final url address:
Here is my code.
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"final url here"]];
[cell.imageView setImageWithURLRequest:request placeholderImage:[UIImage imageNamed:#"GeofencingAlert.png"]
success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage
*image)
{
}
failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) {
NSLog(#"Error: %#",error.description);
}];
Should I first download all of the url stored on the amazon s3 and put them into the NSMutuableArray ? If yes what is the best way to execute multiple requests? What if the user add new record to the table ? Do I have to execute multiple requests again to keep my UITableVIewCell images updated? Thanks in advance.
You should use -(void)viewDidLoad method. Something like this
- (void)viewDidLoad
{
urlPaths = nil;
[httpClient loadUrlsWithSuccess:^(NSArray *results){
urlPaths = results; // urlPaths - class member
[tableView reloadData];
}];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath)indexPath
{
//...
if(urlPaths)
{
NSString *urlPath = urlPaths[indexPath.row];
// load async
}
else
// urls are not loaded yet
imageView.image = placeholderImage;
}
I am working on Game Center for iOS devices using the sandbox environment. I am trying to load the achievement images I set up in iTunes Connect.
I am using loadAchievementDescriptionWithConpletionHandler successfully.
The problem happens when I try to load the image using loadImageWithCompletionHandler.
[self.description loadImageWithCompletionHandler:^(UIImage *image, NSError *error) {
if (!error) {
self.achievementImage = image;
}
}];
The problem is both the UIImage and the error are nil.
It is due to the sandbox environment ? Am I missing something ?
Thanks
I can load the image successfully. It looks like my image format was wrong.
I have deleted the achievement and created a new one using a 512x512 72 dpi image.
Using loadAchievementDescriptionsWithCompletionHandler. I load all the achievementDescriptions from Game Center. Then, I use loadImageWithCompletionHandler to extract the image.
[GKAchievementDescription loadAchievementDescriptionsWithCompletionHandler:^(NSArray *descriptions, NSError *error) {
if (!error) {
for (GKAchievementDescription *achievementDescription in descriptions) {
[self.achievementDescriptionDictionary setObject:achievementDescription
forKey:achievementDescription.identifier];
[achievementDescription loadImageWithCompletionHandler:^(UIImage *image, NSError *error) {
if (!error) {
if (image) {
UIImage *achievementimage = image;
}
}
}];
}
}
}];
I have a sample application with a UITableViewController.
As in the facebook newsfeed, the app is supposed to download a first time X news, and then fetch news progressively as the user scroll.
Here is my implementation :
-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath{
if (indexPath.row == self.newsList.count-PADDLE_BEFORE_FETCHING && !cantFetchMore)
if (!fetching){
fetching = YES;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
[self fetchNews];
});
}
}
(the idea is to start fetching additional news when we reach the N-PADDLE_BEFORE_FETCHING cell, only if we can still fetch some - see below- and if fetching is still not currently running)
and then the implementation of fetchNews :
-(void)fetchNews{
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *url = [NSString stringWithFormat:#"%#%#%#%#%d%#",HOSTNAME,GET_NEWS,[defaults objectForKey:#"oAuthToken"],#"&limit=",FETCH_SIZE_NEWS,[NSString stringWithFormat:#"&offset=%d",self.newsList.count]];
NSURLRequest *request =[[NSURLRequest alloc] initWithURL:[NSURL URLWithString:url]];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
#if DEVELOPMENT_MODE
NSLog(#"News : %#",JSON);
NSLog(#"Response : %#\n Request : %#",response,request);
#endif
//NSLog(#"Number of news fetched : %d",((NSArray*)JSON[#"data"]).count);
for (NSDictionary *d in JSON[#"data"]){
News *new = [[News alloc] initWithDictionary:d];
[self.newsList addObject:new];
new = nil;
}
if ((((NSArray*)JSON[#"data"]).count)%FETCH_SIZE_NEWS !=0) cantFetchMore = YES;
//NSLog(#"%d cantFetch",cantFetchMore);
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[self.tableView reloadData];
fetching = NO;
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
NSLog(#"Request error : %# %# %#",request,error, JSON);
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
fetching = NO;
}];
[operation start];
}
This will fetch FETCH_SIZE_NEWS additional news from the server starting at the good offset which is the current size of the newsList array.
Also, if the count of fetched news % FETCH_SIZE_NEWS is different from 0, that means that we cannot fetch additional news (which will prevent from calling the webservice while scrolling the UITableView).
My issue is that when the fetching is done (exactly when I see the activity wheel running in the status bar), it blocks the GUI, and I cant continue to scroll down from the n-PADDLE_BEFORE_FETCHING cells to the n cells, or even scroll up to the previously loaded cells.
I don't really understand why as AFNetworking is supposed to run asynchronously.
Any ideas?
Thanks,
The for-loop in the completion block is running on the main thread and may be causing the slow-down. Try sending that code to another thread/queue.
As stated above buy Guy Kogus, the processing within the complete block of the AFNetworking operation was adding a bit a freezer on the main thread while scrolling.
Just added
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{//processing block}
Within the complete block (notably the foreach loop) and this is far better.
Thanks