I am trying to load image in my banner as well as URL for that banner and everything is just fine except it apears after a few seconds (10+ sec)
First I was thinking that it may be connection speed but if I hardcoded the line where image needs to apear it apears immediatly.
Here is what I am doing right now.
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithURL:adUrl completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
adJson = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
adImageURL = [NSString stringWithFormat:#"%#", adJson[#"sponsor"][#"sponsor_image"]];
adUrlString = [NSString stringWithFormat:#"%#", adJson[#"sponsor"][#"sponsor_url"]];
[UIApplication sharedApplication].networkActivityIndicatorVisible=NO;
// Set adImage
[[self adBanner]setImage:[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:adImageURL]]]];
// Ad TapGuestures to adImage
UITapGestureRecognizer *adTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(adTapMethod)];
[[self adBanner]setUserInteractionEnabled:YES];
[[self adBanner]addGestureRecognizer:adTap];
}];
[dataTask resume];
As I said, if I do:
[[self adBanner]setImage:[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:#"example.com/img.png"]]]];
and put that code out of NSURLSessionDataTask it apears in no second.
What am I doing wrong and how to get image apear as fast as possible?
The only delays would be from connection & then from the device updating it's display. Remember that the direct call with dataWithContentsOfURL forces the main queue to wait on the image before doing anything. If you execute it with a NSURLSession it would naturally take a bit longer since it isn't set as a high priority.
You should include setImage: as such.
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithURL:adUrl completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
adJson = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
adImageURL = [NSString stringWithFormat:#"%#", adJson[#"sponsor"][#"sponsor_image"]];
adUrlString = [NSString stringWithFormat:#"%#", adJson[#"sponsor"][#"sponsor_url"]];
[UIApplication sharedApplication].networkActivityIndicatorVisible=NO;
// Set adImage
dispatch_async(dispatch_get_main_queue(), ^{
[[self adBanner] setImage:[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:adImageURL]]]];
// Ad TapGuestures to adImage
UITapGestureRecognizer *adTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(adTapMethod)];
[[self adBanner] setUserInteractionEnabled:YES];
[[self adBanner] addGestureRecognizer:adTap];
});
}];
[dataTask resume];
Related
I have implemented a UITableview in VC1, and I want to display some JSON data in the cell. I have implemented a Model class to pass the data to the table view.
dispatch_async(dispatch_get_main_queue(), ^{
NSURLSession*session=[NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithURL:[NSURL URLWithString:#"https://itunes.apple.com/search?term=music"] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSLog(#"%#", json);
NSArray *entryarr = [json objectForKey:#"results"];
TableClass *tc = [[TableClass alloc] init];
for (NSDictionary *appDict in entryarr) {
//setting title
NSString *str = [appDict objectForKey:#"artistName"];
[tc setTittle:str];
NSLog(#"artist Name=%#",tc.tittle);
//setting Subtitle
NSString *sub = [appDict objectForKey:#"country"];
[tc setSubtittle:sub];
NSLog(#"artist Name=%#",tc.subtittle);
//image
NSString *imageStr = [appDict objectForKey:#"artworkUrl60"];
NSURL *imageURL = [NSURL URLWithString:imageStr];
[tc setImage:imageStr];
NSData *imageData =[[NSData alloc] initWithContentsOfURL:imageURL];
//[self.imageArray addObject:imageData];
[_tableArray addObject:tc];
NSLog(#"%# name of tittle",[_tableArray objectAtIndex:0]);
}
NSLog(#"%lu %lu %lu",(unsigned long)self.tableArray.count,(unsigned long)self.tableArray.count,(unsigned long)self.tableArray.count);
[self.tableView reloadData];
}];
[dataTask resume];
});
But, while accessing it to the cell I am getting the last element in the array.
TableClass *tableclassModel = [self.tableArray objectAtIndex:indexPath.row];
cell.textLabel.text = tableclassModel.tittle;
cell.detailTextLabel.text = tableclassModel.subtittle;
cell.imageView.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:tableclassModel.image]]];
Why is it so...? How can I do it?
You are changing the value of same tc object again and again. Since you have declared tc object outside for loop, there will be only one tc. You are adding it to _tableArray after making the required changes in the first iteration of the loop. In the second iteration, you are changing the value of same tc object used in first iteration and adding it to _tableArray again. This will update the first object also with the new values. This goes on and finally your _tableArray will contain n number of tc objects with the same values (i.e last updated value)
Give the declaration inside the for loop
dispatch_async(dispatch_get_main_queue(), ^{
NSURLSession*session=[NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithURL:[NSURL URLWithString:#"https://itunes.apple.com/search?term=music"] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSLog(#"%#", json);
NSArray*entryarr=[json objectForKey:#"results"];
for (NSDictionary*appDict in entryarr) {
TableClass*tc=[[TableClass alloc]init];
//setting tittl
NSString*str=[appDict objectForKey:#"artistName"];
[tc setTittle:str];
NSLog(#"artist Name=%#",tc.tittle);
//setting Subtittle
NSString*sub=[appDict objectForKey:#"country"];
[tc setSubtittle:sub];
NSLog(#"artist Name=%#",tc.subtittle);
//image
NSString*imageStr=[appDict objectForKey:#"artworkUrl60"];
NSURL*imageURL=[NSURL URLWithString:imageStr];
[tc setImage:imageStr];
NSData*imageData=[[NSData alloc]initWithContentsOfURL:imageURL];
//[self.imageArray addObject:imageData];
[_tableArray addObject:tc];
NSLog(#"%# name of tittle",[_tableArray objectAtIndex:0]);
}
NSLog(#"%lu %lu %lu",(unsigned long)self.tableArray.count,(unsigned long)self.tableArray.count,(unsigned long)self.tableArray.count);
[self.tableView reloadData];
}];
[dataTask resume];
});
I have a TableView with customCells, when user press Start button on some cell the loading starts. There are many such cells, so I need to implement this downloading in parallel (asynchronously).
For image downloading and updating the cell in Table view I use next code:
#define myAsyncQueue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
I include this method into the async queue, that I supposed should enable parallel downloading of images.
- (void)didClickStartAtIndex:(NSInteger)cellIndex withData:
(CustomTableViewCell*)data
{
dispatch_async(myAsyncQueue, ^{
self.customCell = data;
self.selectedCell = cellIndex;
ObjectForTableCell* tmp =[self.dataDictionary objectForKey:self.names[cellIndex]];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:tmp.imeageURL]
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval:60.0];
self.connectionManager = [[NSURLConnection alloc] initWithRequest:urlRequest delegate:self];
});
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
self.urlResponse = response;
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
NSDictionary *dict = httpResponse.allHeaderFields;
NSString *lengthString = [dict valueForKey:#"Content-Length"];
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
NSNumber *length = [formatter numberFromString:lengthString];
self.totalBytes = length.unsignedIntegerValue;
self.imageData = [[NSMutableData alloc] initWithCapacity:self.totalBytes];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[self.imageData appendData:data];
self.customCell.progressView.progress = ((100.0/self.urlResponse.expectedContentLength)*self.imageData.length)/100;
float per = ((100.0/self.urlResponse.expectedContentLength)*self.imageData.length);
self.customCell.realProgressStatus.text = [NSString stringWithFormat:#"%0.f%%", per];
}
I tried to set this block to queue - main queue - cause its the place where image is already downloaded,
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
dispatch_async(dispatch_get_main_queue(), ^{
self.customCell.realProgressStatus.text = #"Downloaded";
UIImage *img = [UIImage imageWithData:self.imageData];
self.customCell.image.image = img;
self.customCell.tag = self.selectedCell;
});
[self.savedImages setObject:img forKey:self.customCell.nameOfImage.text];
NSNumber *myNum = [NSNumber numberWithInteger:self.selectedCell];
[self.tagsOfCells addObject:myNum];
}
Without all queues(when I comment it)all works properly - but just 1 downloading at a ones.
But when I tried to implement code with queues as a result it doesn't download anything. I understand that I did smh wrong but I can't define it.
Thanks a lot for any help in advance.
If your looking out for starting it form basics I guess you should start with NSURLSession as NSURLConnection most of implementation had been deprecated and won't be available after iOS 9. For complete reference URL Session Programming Guide and tutorial
Coming back to your question you should do something similar to this took it from tutorial
// 1
NSURLSessionDownloadTask *getImageTask =
[session downloadTaskWithURL:[NSURL URLWithString:imageUrl]
completionHandler:^(NSURL *location,
NSURLResponse *response,
NSError *error) {
// 2
UIImage *downloadedImage =
[UIImage imageWithData:
[NSData dataWithContentsOfURL:location]];
//3
dispatch_async(dispatch_get_main_queue(), ^{
// do stuff with image
_imageWithBlock.image = downloadedImage;
});
}];
// 4
[getImageTask resume];
But my personal recommendation is go for AFNetworking which is best for iOS networking and widely used/tested in iOS app world.
For image download using AFNetworking
[_imageView setImageWithURLRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://i.imgur.com/fVhhR.png"]]
placeholderImage:nil
success:^(NSURLRequest *request , NSHTTPURLResponse *response , UIImage *image ){
NSLog(#"Loaded successfully: %d", [response statusCode]);
}
failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error){
NSLog(#"failed loading: %#", error);
}
];
EDIT : Async downloading using concurrency
// get main dispact queue
dispatch_queue_t queue = dispatch_get_main_queue();
// adding downloading task in queue using block
dispatch_async(queue, ^{
NSData* imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:imageURL]];
UIImage* image = [[UIImage alloc] initWithData:imageData];
// after download compeletes geting main queue again as there can a possible crash if we assign directly
dispatch_async(dispatch_get_main_queue(), ^{
_imageWithBlock.image = image;
});
});
Use this sample code from Apple to solve your problem of lazy loading.
I have seen this question somewhat answered here but in my case I am using NSURLSession to display images. These images are uploaded by user or scanned into a database using a script.
In this case writing exception URL's (NSExceptionDomains) won't work because the image is hosted by a user on their site or some other site. If I allow NSAllowsArbitraryLoads will I still be able to be approve for App Store since I am not implementing the best practices of ATS?
I am not sure the best way to proceed. Any input would be appreciated!
Here is the code I am using.
NSString *thumbnail_url = [tempObject objectForKey:#"image"];
NSURL *url = [NSURL URLWithString:thumbnail_url];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDownloadTask *downloadPhotoTask = [session downloadTaskWithURL:url completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
NSData *imageData = [[NSData alloc] initWithContentsOfURL:location];
dispatch_async(dispatch_get_main_queue(), ^{
cell.tableImageView.image = [UIImage imageWithData:imageData];
});
}];
[downloadPhotoTask resume];
Yes, you'll pass Review even with this parameter.
We have uploaded many builds since iOS9 SDK with NSAllowsArbitraryLoads set to YES.
P.S.: Your code should better look like this:
cell.thumbnailURL = URL;
__weak typeof(cell) weak
NSURLSessionDownloadTask *downloadPhotoTask = [session downloadTaskWithURL:URL completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
NSData *imageData = [[NSData alloc] initWithContentsOfURL:location];
UIImage *image = [UIImage imageWithData:imageData];
dispatch_async(dispatch_get_main_queue(), ^{
if (weakCell.thumbnailURL != URL) {
return;
}
weakCell.tableImageView.image = image;
});
}];
[downloadPhotoTask resume];
I have some code that gets an image from a web page and displays it in an ImageView. But the image loads very slowly for some reason I don't really understand! Through my logging I can see that all the data for the image (base64 string) arrives pretty instantly, yet it takes about 12 - 15 seconds for the image to appear in the ImageView.
I find this very strange because I used an NSStream to get the data for the image in a different method and the image loaded as soon as all the data arrived. But with this URLSession method its taking longer for the image to load. This doesn't really make sense! This method shouldn't affect how the ImageView loads that data.
Has anybody any ideas why this might be happening?
heres the code:
- (void)postMethod:(NSDictionary *)numDict
{
NSURL *url = [NSURL URLWithString:#"http://theWebAddress.com/aPage.php"]; // add url to page
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:config];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
request.HTTPMethod = #"POST";
NSError *error = nil;
NSData *data = [NSJSONSerialization dataWithJSONObject:numDict options:kNilOptions error:&error];
NSLog(#"%#", numDict);
if (!error)
{
NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromData:data completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSDictionary *diction = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
for (id key in diction)
{
if ([key isEqualToString:#"text"])
{
NSLog(#"data is text");
self.messageLabel.text = diction[#"text"];
break;
}
else if ([key isEqualToString:#"image"])
{
NSLog(#"data is an image");
// gets the base64 string pretty instantly but takes 12 - 15 seconds to pop up in the imageView
NSData *ImgData = [[NSData alloc] init];
ImgData = [[NSData alloc] initWithBase64EncodedString:diction[#"image"] options:1];
self.ImageView.image = [UIImage imageWithData:ImgData];
break;
}
}
}];
[uploadTask resume];
}
}
many thanks!
Your completion handler might be operating on a background thread. UI updates should always work on the main thread. Put a break point at
self.ImageView.image = [UIImage imageWithData:ImgData];
and see if it is on the main thread. If not, dispatch it to the main thread before you set the ImageView.image:
dispatch_async(dispatch_get_main_queue(), ^{
self.ImageView.image = [UIImage imageWithData:ImgData];
});
You can try to use SDWebImage https://github.com/rs/SDWebImage and all you need is to set the image in imageView like this:
[cell.imageView setImageWithURL:[NSURL URLWithString:#"http://www.domain.com/path/to/image.jpg"]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
You are firstly downloading image and then showing image.You can download image by using lazy loading.
For this you can use EgoImageView not uiimageview.
self.ImageView.imageURL=[NSURL URLWithString:
here self.ImageView is of egoimageview type.
you can get this class from github.
https://github.com/enormego/EGOImageLoading
I have been trying to download image/txt but i need to do it until that url exists & download that image/txt file,so i keep calling same method again & when i set debug point i see this .
If url is right than i do not see any queue in debug navigator because it is not calling method again. i referred AFNetworking library to the same but i guess it`s working in same way as i am doing in NSURLSession,right?
Case:- I check for url if exists or not, so if it`s exists than load both urls(time.txt & image.png), otherwise call WebService(XmlParser) & keep check for urls for following files.
time.txt+image.png
or
tryagain.txt
show whichever exists.
Also checked this AFNetworking Question but it didnt helped because i do not want to add number of operations. i want to load file whichever exists.
Because Operations will be completed whether it is success or fail in AFNetworking/NSURLSession.
-(void)downloading
{
NSString *imageUrl = [NSString stringWithFormat:#"%#",txtNumber.text];
NSURLSessionConfiguration *sessionConfig =[NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session =[NSURLSession sessionWithConfiguration:sessionConfig
delegate:self
delegateQueue:nil];
NSURLSessionDownloadTask *getImageTask = [session downloadTaskWithURL:[NSURL URLWithString:imageUrl]
completionHandler:^(NSURL *location,
NSURLResponse *response,
NSError *error)
{
UIImage *downloadedImage =[UIImage imageWithData:[NSData dataWithContentsOfURL:location]];
dispatch_async(dispatch_get_main_queue(), ^{
// do stuff with image
if (downloadedImage)
{
carImgView.image = downloadedImage;
result = TRUE;
}
else
{
result = FALSE;
[self tryagain];
}
});
}];
[getImageTask resume];
}
-(void)tryagain
{
NSString *strImg = [[NSString stringWithFormat:#"%#",gblPolicyNo] stringByAppendingString:FilePolStatus];
NSString *apiImage = [NSString stringWithFormat:#"http://moviepoint.info/%#/%#",FolderPolStatus,strImg];
NSURL *aImgUrl = [NSURL URLWithString:apiImage];
// 2
NSURLSessionConfiguration *sessionConfig =
[NSURLSessionConfiguration defaultSessionConfiguration];
// 3
tryAgainSession =[NSURLSession sessionWithConfiguration:sessionConfig
delegate:self
delegateQueue:nil];
// 1
getTryAgainTask = [tryAgainSession downloadTaskWithURL:aImgUrl
completionHandler:^(NSURL *location,
NSURLResponse *response,
NSError *error)
{
// 2
UIImage *downloadedImage =[UIImage imageWithData:[NSData dataWithContentsOfURL:location]];
//3
dispatch_async(dispatch_get_main_queue(), ^{
// do stuff with image
if (downloadedImage)
{
[policyImgWebView loadData:[NSData dataWithContentsOfURL:location] MIMEType:nil textEncodingName:nil baseURL:nil];
NSLog(#"YES");
}
else
{
NSLog(#"NO");
[self performInBackground];
}
});
}];
// 4
[getTryAgainTask resume];
}
Please Correct me if i am doing wrong & Help me to solve this problem.
Solved by taking One Global NSURLSession