I am trying to download an array of images, but I never receive the image callback:
for (int i = 0; i < self.apartments.count; i++) {
NSLog(#"downloading another image");
ATProperty *aProp = self.apartments[i];
if (aProp.aptImages.count > 0) {
NSURL *url = [NSURL URLWithString:aProp.aptImages[0]];
NSLog(#"url string: %#", aProp.aptImages[0]);
NSURLRequest *request = [NSURLRequest requestWithURL:url];
UIImage *placeholderImage = [UIImage imageNamed:#"loading_apt.png"];
UIImageView *anotherImage;
NSLog(#"about to start req");
[anotherImage setImageWithURLRequest:request
placeholderImage:placeholderImage
success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image)
{
NSLog(#"got an IMAGE ");
self.imagesArray[i] = image;
if (self.apartments[0] == aProp) [self setupDraggableView:image];
} failure:nil];
} else {
UIImage *defImage = [UIImage imageNamed:#"default_apt.png"];
if (self.apartments[0] == aProp) [self setupDraggableView:defImage];
[self.imagesArray addObject:defImage];
}
}
As you can see, I don't actually want to set an imageview, I just want to put the UIImage into an array for later use. In this case, the "got an IMAGE" log statement is never received. Why would this be the case? The url is valid. Also, is this somewhat like a javascript closure? For example if I have a total of 5 apartments in the array, when I do the first callout (i = 0) and get the success callback will i be the value in which the callout was instantiated (i.e. 1)?
anotherImage is nil, so it never loads. try like that:
UIImageView *anotherImage = UIImageView.new;
Related
From my web service i am getting multiple sized images. I m loading them in TableView. When i m loading them new images which haven't cached yet flickers between placeholder and original image, or sometimes the image appear as smaller than the usual size. But just a bit scrolling down or up actually fix the problem but i want them to be at original size from the beginning. But it would appear in original shape from the next time i suppose because the picture was already cached
Initially
After a bit scrolling
My code in cellForRowAtIndexPath:
[cell.image sd_setImageWithURL:[NSURL URLWithString:[NSMutableString stringWithFormat:#"%#app/media/access/pictures?p=%#",baseurl,data.picPath]]
placeholderImage:[UIImage imageNamed: #"image_loader.gif"]];
then in heightForRowAtIndexPath:
NSURL *filePath = [NSURL URLWithString:[NSMutableString stringWithFormat:#"%#app/media/access/pictures?p=%#",baseurl,data.picPath]];
NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:filePath];
UIImage *image = [[SDImageCache sharedImageCache] imageFromDiskCacheForKey:key];
self.img = image;
if (self.img.size.width > CGRectGetWidth(self.view.bounds)) {
CGFloat ratio = self.img.size.height / self.img.size.width;
return CGRectGetWidth(self.view.bounds) * ratio+140;
} else {
return self.img.size.height+180;
}
I have visited https://github.com/rs/SDWebImage and in their common problem section they have mentioned about the problem but the suggested solution didn't work for me!!!
Actually what i did, i checked if the image was cached already and if not i have downloaded it asynchronously under heightForRowAtIndexPath:
if(image != NULL)
{
self.img = image;
}
else
{
SDWebImageDownloader *downloader = [SDWebImageDownloader sharedDownloader];
[downloader downloadImageWithURL:filePath
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
self.img = image;
}
}];
}
I have a method that I'm calling from cell in the viewForRowAtIndexPath method.
The method containts only this code
NSURL *imageURL = [NSURL URLWithString:urlString];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
UIImage *image = [UIImage imageWithData:imageData];
dispatch_async(dispatch_get_main_queue(), ^(void){
completionBLock(image);
});
Images are loading very slowly and sometimes not at all. Everytime the cell goes back into view the method gets called again.
This is in my cellForIndexPathRow
[Method getImage:brochureModel.brochureCoverUrl Oncompletion:^(UIImage *image) {
cell.imageView.image = image;
}];
How can I download images from a url and display it in a collection view reusable cells.
Note: I'm trying to not use any third party libraries for my app
My problem is with the caching of the images. I tried this with self.imageCache being a mutableDictionary.
if (self.imageCache[urlString]) {
UIImage *imageFromCache = self.imageCache[urlString];
onImageDidLoad(imageFromCache);
} else {
[API fetchImageFromUrl:urlString onDidLoad:^(UIImage *image) {
self.imageCache[urlString] = image;
onImageDidLoad(image);
}];
}
The problem is that I am calling multiple webservices on my homepage and the webservice is returning me the images and text from the server. During this process the UI become fully unresponsive for say 1-2 minutes which is looking very bad as I cant do anything. I heard about dispatch and tried to implement it but I dont get any results.May be I am doing something wrong.
What I want now that I want to that I want to run this process in background So that a user can interact with the UI during the fetching operation from the server. I am implementing my code just tell me where to use dispatch.
-(void)WebserviceHomeSlider{
if([AppDelegate appDelegate].isCheckConnection){
//Internet connection not available. Please try again.
UIAlertView *alertView=[[UIAlertView alloc] initWithTitle:#"Internate error" message:#"Internet connection not available. Please try again." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alertView show];
return;
}else {
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.responseSerializer.acceptableContentTypes = [manager.responseSerializer.acceptableContentTypes setByAddingObject:#"text/html"];
[manager GET:ServiceUrl#"fpMainBanner" parameters:nil success:^(AFHTTPRequestOperation *operation,id responseObject)
{
//NSLog(#"JSON: %#", responseObject);
arrSlider = [responseObject objectWithJSONSafeObjects];
[_slideshow setTransitionType:KASlideShowTransitionSlide];
_slideshow.gestureRecognizers = nil;
[_slideshow addGesture:KASlideShowGestureSwipe];
// [_slideshow addImagesFromResources:[self getImages]]; // Add
// [_slideshow addTextFromResources:[self getText]];
// [slideShow subtextWithImages:[self getsubText]];
[_slideshow addImagesFromResources:[self getImages] stringArray:[self getText] stringsubArray:[self getsubText]];
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
}
}
Just tell me where to use dispatch or edit my code using dispatch if possible. I have gone through some examples but still my concept is not clear. Which dispatch method id best (DEFAULT or BACKGROUND). I will be very thankful to You.
This is the code you are looking for . Just tell me where to edit it using dispatch
-(NSArray *)getText{
NSMutableArray *textArr = [[NSMutableArray alloc] init];
for(int i=0; i<[arrSlider count];i++)
{
texxt=[[arrSlider objectAtIndex:i ]valueForKey:#"title" ];
[textArr addObject:[texxt uppercaseString]];
}
return textArr;
}
-(NSArray *)getsubText{
NSMutableArray *subtext = [[NSMutableArray alloc] init];
for(int i=0; i<[arrSlider count];i++)
{
subbtext=[[arrSlider objectAtIndex:i ]valueForKey:#"tagline_value" ];
if(i==8)
{
subbtext=#"MAKE YOURSELF STAND OUT GET YOUR FREE CARDS!";
}
NSLog(#"subtext is,,.,.,,.,%#.%#",#"k",subbtext);
[subtext addObject:[subbtext uppercaseString]];
}
return subtext;
}
-(NSArray *)getImages
{
NSMutableArray *mArr = [[NSMutableArray alloc] init];
for(int i=0; i<[arrSlider count];i++)
{
pathh=[[arrSlider objectAtIndex:i ]valueForKey:#"filepath" ];
NSString *newString = [pathh stringByReplacingOccurrencesOfString:#" " withString:#"%20"];
NSURL *imageURL = [NSURL URLWithString:newString];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
UIImage *originalImage = [UIImage imageWithData:imageData];
CGSize destinationSize = CGSizeMake(320, 158);
UIGraphicsBeginImageContext(destinationSize);
[originalImage drawInRect:CGRectMake(0,0,destinationSize.width,destinationSize.height)];
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// [whotshotimgview setImage:image];
[mArr addObject: img];
}
return mArr;
}
[_slideshow addImagesFromResources:[self getImages] stringArray:[self getText] stringsubArray:[self getsubText]];
The problem here is that you didn't showed implementation of addImagesFromResources method. You probably have to implement GCD in this method because i guess you are fetching images and setting on UI by this your main thread is getting blocked.
You are trying to access the UIView from thread other than the main which causes the UI unresponsiveness.
Please use this:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[_slideshow setTransitionType:KASlideShowTransitionSlide];
_slideshow.gestureRecognizers = nil;
[_slideshow addGesture:KASlideShowGestureSwipe];
// [_slideshow addImagesFromResources:[self getImages]]; // Add
// [_slideshow addTextFromResources:[self getText]];
// [slideShow subtextWithImages:[self getsubText]];
[_slideshow addImagesFromResources:[self getImages] stringArray:[self getText] stringsubArray:[self getsubText]];
});
What I think is that your UI hangs due to downloading of image.
So you should use SDWebImage library that can help you to cache image and also prevent the UI getting hang. Using SDWebImage you can show the loader for the time the image is not loaded and it will cache the image. So from next time no image will be downloaded and also the UI will not hang. The link for complete reference to SDWebImage is :https://github.com/rs/SDWebImage
This block blocking your main UI to perform tasks so update this block as follow,
for(int i=0; i<[arrSlider count];i++)
{
pathh=[[arrSlider objectAtIndex:i ]valueForKey:#"filepath" ];
NSString *newString = [pathh stringByReplacingOccurrencesOfString:#" " withString:#"%20"];
dispatch_queue_t myQueue = dispatch_queue_create("My Queue",NULL);
dispatch_async(myQueue, ^{
NSURL *imageURL = [NSURL URLWithString:newString];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
UIImage *originalImage = [UIImage imageWithData:imageData];
CGSize destinationSize = CGSizeMake(320, 158);
UIGraphicsBeginImageContext(destinationSize);
[originalImage drawInRect:CGRectMake(0,0,destinationSize.width,destinationSize.height)];
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// [whotshotimgview setImage:image];
[mArr addObject: img];
dispatch_async(dispatch_get_main_queue(), ^{
// If you want to refresh your UI simultaniously, you can write statement for that i.e
// yourImageView.image = img;
// Otherwise remove this queue
});
});
}
The main thread should never be used for long processing. Its there for the UI.
AFNetworking provides asynchronous functionality for downloading files. You should also be careful of thrashing the network layer with too many simulatanous downloads. I think 4 or 5 is the max the last time I tested.
So the flow of execution should be as follows.
The controller sets up the slideshow, sends the downloading of the images to a background thread(automatically handled by AFNetworking), passing a completion block to it. In this completion block you need to dispatch the work back to the main thread using GCD, dispatch_async to dispatch_get_get_main_queue
Keep your main thread's run loop available for user interaction.
It is because the images are loading in another thread and you are populating your slider right after getting the data.
It is better to implement this protocol method of KASlidershow to use the slideshow in a more memory efficient way.
(UIImage *)slideShow:(KASlideShow *)slideShow imageForPosition:(KASlideShowPosition)position
{
pathh=[[arrSlider objectAtIndex:i ]valueForKey:#"filepath" ];
NSString *newString = [pathh stringByReplacingOccurrencesOfString:#" " withString:#"%20"];
dispatch_queue_t myQueue = dispatch_queue_create("My Queue",NULL);
dispatch_async(myQueue, ^{
NSURL *imageURL = [NSURL URLWithString:newString];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
UIImage *originalImage = [UIImage imageWithData:imageData];
CGSize destinationSize = CGSizeMake(320, 158);
UIGraphicsBeginImageContext(destinationSize);
[originalImage drawInRect:CGRectMake(0,0,destinationSize.width,destinationSize.height)];
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// [whotshotimgview setImage:image];
[mArr addObject: img];
return img;
});
}
i didn't try the code but i hope it will work with little changes in variable names. Good luck
I have successfully retrieved the image (lets say user profile pic) from parse, and i'm displaying it in the ios app. But if there is no image in the file column, then i want to display default image which is stored in the locally (not in parse), not successful.
My simple question is, how to check if there is image or not ? Below is the simple code;
PFFile *userImage = [object objectForKey:#"profilePic"]; __block UIImage *userProfilePic =[[UIImage alloc]init];
[userImage getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
if (!error) {
//this block will execute only if there is image
if(data) {
userProfilePic = [UIImage imageWithData:data];
userPic.image=userProfilePic;
}
//this block is not at all executing. This block should execute when user doesn't have their profile uploaded
if(!data) {
UIImage *userProfileDefaultPic = [[UIImage alloc]init];
userProfileDefaultPic=[UIImage imageNamed:#"defaultprofile.png"];
userPic.image=userProfileDefaultPic;
}
}
}];
Hope you can help me. Thanks in advance.
Pradeep
At last found the solution !
(!data) will not execute , instead we need to check if the PFFile and the UIImage to store PFFile has image or not, like below;
PFFile *userImage = [object objectForKey:#"profilePic"];
__block UIImage *userProfilePic =[[UIImage alloc]init];
if(userImage && ![userProfilePic isEqual:[NSNull null]]) //This is important to check if the file if image is there or not and if not, then display default pic in else part
{
[userImage getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
if(!error)
{
userProfilePic = [UIImage imageWithData:data];
userPic.image=userProfilePic;
}
}];
}
else
{
userProfilePic =[UIImage imageNamed:#"defaultprofilepic.png"];
userPic.image=userProfilePic;
}
Your code already does that. You just need to set your image on the main thread and not background thread.
if(!data) { [dispatch_async(dispatch_get_main_queue(), ^{
UIImage *userProfileDefaultPic = [[UIImage alloc]init];
userProfileDefaultPic=[UIImage imageNamed:#"defaultprofile.png"];
userPic.image=userProfileDefaultPic;
}}];
We were using AFNetworking only to load images like this:
[thumbnailImage setImageWithURL:_item.thumbnailUrl];
and it worked fine. But now we've pulled in the rest of the project it has stopped working. It was just showing the background color. So I tried using this and it loads the placeholder but never loads the image.
UIImage* placeholder = [UIImage imageNamed:#"placeholder"];
[thumbnailImage setImageWithURL:_item.thumbnailUrl placeholderImage:placeholder];
I thought I might be able to see what the problem was so I tried:
NSURLRequest* urlRequest = [NSURLRequest requestWithURL:_item.thumbnailUrl cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:30];
[thumbnailImage setImageWithURLRequest:urlRequest placeholderImage:[UIImage imageNamed:#"placeholder"] success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image)
{
NSLog(#"Loaded Image");
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error)
{
NSLog(#"Failed to load Image");
}];
and it seems that it's not getting the return type that it's expecting. It says Expected content type image/png, got binary/octet-stream I'm pulling the image from Amazon S3 so I don't know if I have control over the type.
I wasn't able to change the data type so I overrode the acceptableContentTypes method in AFNetworking by adding the following files to my project.
AFImageRequestOperation+OctetMIMEType.h
//
// AFImageRequestOperation+OctetMIMEType.h
// iQ.fileShare
//
// Created by Poole, Patrick on 4/25/13.
//
//
#import "AFImageRequestOperation.h"
#interface AFImageRequestOperation (OctetMIMEType)
#end
AFImageRequestOperation+OctetMIMEType.m
//
// AFImageRequestOperation+OctetMIMEType.m
// iQ.fileShare
//
// Created by Poole, Patrick on 4/25/13.
//
//
#import "AFImageRequestOperation+OctetMIMEType.h"
#implementation AFImageRequestOperation (OctetMIMEType)
+ (NSSet *)acceptableContentTypes {
return [NSSet setWithObjects:#"binary/octet-stream",#"image/tiff", #"image/jpeg", #"image/gif", #"image/png", #"image/ico", #"image/x-icon" #"image/bmp", #"image/x-bmp", #"image/x-xbitmap", #"image/x-win-bitmap", nil];
}
#end
AFNetworking provides a method to add acceptable content types in 1.3.3, I'm not sure what other versions support this
[AFImageRequestOperation addAcceptableContentTypes:[NSSet setWithObjects:#"binary/octet-stream", nil]];