My app displays a lot of images, so I am using page control to display the images in single controller.
User can able to see all the images by swiping.
I am using AFNetworking Library to show the good performance. I have used the property setImageWithUrl
// This is myCode
NSURL *myUrl = [NSURL URLWithString:#"http://mdb.scicloudsolutions.com:8001/sites/default/files/landscape_design_1.jpg"];
[imageView setImageWithUrl:myUrl];
it's not showing at first time only..
When I Print url and image
NSLog(#"Url is .. : %# image. is : %#",myUrl,imageView.image);
Response:
am receiving the url value but not image value.
It shows image is null at first time.
How to solve?
Here You Set [imageView setImageWithUrl:myUrl]; So it take time to load an Image in to ImageView.
Use SDWebImage Library For Better. Just Simple Download this Library From Link and Set Image like as
[imageView sd_setImageWithURL:url
placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
Use following solution for this:
#property (nonatomic, strong) NSMutableData *data
#property (nonatomic, strong) NSURLConnection *connection;
NSURLRequest *request = [NSURLRequest requestWithURL:url
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
Add these delegate methods to your view controller:
- (void)connection:(NSURLConnection *)connection
didReceiveData:(NSData *)incrementalData {
if (data==nil) {
data = [[NSMutableData alloc] initWithCapacity:2048];
}
[data appendData:incrementalData];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)aConnection {
connection = nil;
UIImage *image = [UIImage imageWithData:data];
// Set above image to your imageView.
data = nil;
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
/*============= YOU ARE ON BACK GROUND THREAD ==================*/
UIImageView *yourImageView=[[UIImageView alloc] init];
UIImage *img=[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:#"pass your url here"]]];
dispatch_async(dispatch_get_main_queue(), ^(void) {
/*============= YOU ARE ON MAIN THREAD ==================*/
yourImageView.image=img;
});
});
Related
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.
this is my code to load my images.
NSString *urltest = test[#"images"][#"items"][i][#"url"];
url = [NSURL URLWithString:urltest];
data = [NSData dataWithContentsOfURL:url];
img = [[UIImage alloc] initWithData:data];
[self.imgView setImage:img];
I would like to know when my is loaded. How can i do that?
In XCode Debugger section (located at the bottom left of the XCode), you can check image object value by inserting breakpoint there. You can also preview its value by clicking on preview button (human eye look like button).
You can achive it by,
First write this code
[self performSelectorOnMainThread:#selector(LoadImage:) withObject:#"MY URL STRING" waitUntilDone:NO];
And then
-(void)LoadImage:(NSString *) urlString
{
/// do something when image is loading
NSURL *imgURL=[NSURL URLWithString:urlString];
NSData *imgData=[NSData dataWithContentsOfURL:imgURL];
[self performSelectorInBackground:#selector(setImage:) withObject:imgData];
}
-(void)setImage:(NSData *) imgData;
{
/// do something when image loading is over
myImageView.image=[UIImage imageWithData:imgData];
}
And if you want to know your image is successfully downloaded or not then use following code
[NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:[NSURL URLWithString::#"MY URL STRING"]] queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse* response, NSData* data, NSError* error){
if(error)
{
// Error Downloading image data
}
else
{
[myImageView setImage:[UIImage imageWithData:data]];
}
}];
I have the following method, which basically loads an array of image data into an array:
-(void)loadImages:(NSMutableArray*)imagesURLS{
//_indexOfLastImageLoaded = 0;
[_loadedImages removeAllObjects];
_loadedImages = [[NSMutableArray alloc]init];;
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
for (int i=0; i<imagesURLS.count;i++){
NSLog(#"loading image for main image holder at index %i",i);
NSData *imgData = [NSData dataWithContentsOfURL:[imagesURLS objectAtIndex:i]];
UIImage *img = [UIImage imageWithData:imgData];
[_loadedImages addObject:img];
//_indexOfLastImageLoaded++;
}
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"_loadedImages download COMPLETE");
});
});
}
I want to be able to stop it when, for example, a user moves away from the view controller that these images are being loaded in. What is the best way to do so?
Thanks!
You can't cancel NSData dataWithContentsOfUrl:. The best way to achieve cancelable, asynchronous downloading is by using NSURLConnection and the NSURLConnectionDataDelegate.
You set up an NSMutableData object to accumulate all the data as it comes in in chunks. Then when all the data has arrived, you create your image and use it.
.h
#interface ImageDownloader : NSObject <NSURLConnectionDataDelegate>
#property (strong, nonatomic) NSURLConnection *theConnection;
#property (strong, nonatomic) NSMutableData *buffer;
#end
.m
-(void)startDownload
{
NSURL *imageURL = [NSURL URLWithString: #"http://example.com/largeImage.jpg"];
NSURLRequest *theRequest = [NSURLRequest requestWithURL: imageURL];
_theConnection = [[NSURLConnection alloc] initWithRequest: theRequest delegate: self startImmediately: YES];
}
-(void)cancelDownload
{
// CANCELS DOWNLOAD
// THROW AWAY DATA
[self.theConnection cancel];
self.buffer = nil;
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
// INITIALIZE THE DOWNLOAD BUFFER
_buffer = [NSMutableData data];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
// APPEND DATA TO BUFFER
[self.buffer appendData: data];
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// DONE DOWNLOADING
// CREATE IMAGE WITH DATA
UIImage *theImage = [UIImage imageWithData: self.buffer];
}
If you want to be more flexible with cancel request i advice you to use NSOperationQueue instead of pushing all request in a row.
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue setMaxConcurrentOperationCount:1];
for (int i=0; i<allImagesCount; i++) {
[queue addOperationWithBlock:^{
// load image
}];
}
// for canceling operations
[queue cancelAllOperations];
In you current code you can also define static field and check in for loop, but the best way will be using SDWebImage - https://github.com/rs/SDWebImage for loading image asynch.
I'm making a request to a URL to download an image. I receive it as an NSData object, but when I try to convert it to a UIimage with imageWithData: the UIImage is not loaded. Here's my Code
__block ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
__weak typeof(request)weakRequest = request;
__block UIImage * imager;
__block NSData * image;
[request setCompletionBlock:^{
[self parseXMLFileAtFileId:[weakRequest responseData]];
if (!files){
cell.correctImage.image = [UIImage imageNamed:#"sidebarPlaceholder"];
}
else{
NSURL *url = [NSURL URLWithString:[self downloading:[files objectAtIndex:0]]];
__block ASIHTTPRequest *requester = [ASIHTTPRequest requestWithURL:url];
__weak typeof(request)weakRequesting = requester;
[requester setCompletionBlock:^{
image = [weakRequesting responseData];
imager = [UIImage imageWithData:image];
cell.correctImage.image = imager;
[cell layoutIfNeeded];
}];
[requester startAsynchronous];
}
}];
Previously I got it working by explicitly stating the size of the UIImageView, but that should be taken care of by the UITableviewCell. How do I get the image to show up?
Try to run these block of code in main thread as it appeared you are trying to set the image and updating layout in background thread. Use following NSThread method right after
UIImage *imager = [UIImage imageWithData:image]
[self performSelectorOnMainThread:#selector(updateImage:) withObject:imager waitUntilDone:false];
// Define method to update the image
- (void)updateImage: (UIImage *)paramImage {
cell.correctImage.image = paramImage;
[cell layoutIfNeeded];
}
I am sending an asynchronous http GET request and the completionHandler is being called correctly. Code in the callback like NSLog gets run as I can see the output in the logs. However, the lines: self.imageView.image = nil; doesn't seem to go into effect until a few seconds after the NSLog statement "got here". Does anyone know what's happening? The sample code is below:
In ViewController.m:
#interface ViewController ()
#property (weak, nonatomic) IBOutlet UIImageView *imageView;
#end
#implementation ViewController
#synthesize imageView = _imageView;
-void viewDidLoad {
// ImageView
UIImage *image = [UIImage imageNamed:#"test.jpg"];
self.imageView.backgroundColor = [UIColor blackColor];
self.imageView.clipsToBounds = YES;
self.imageView.image = image;
}
-void test {
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:#"http://someurl"] cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:10];
[request setHTTPMethod:#"GET"];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:request queue:queue
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
self.imageView.image = nil;
NSLog(#"got here");
}];
}
#end
Ah, looks like setting the queue param to [NSOperationQueue mainQueue] fixed as it is here:
NSURLConnection sendAsynchronousRequest:queue:completionHandler: making multiple requests in a row?
As Undept suggested, call thoses lines on main thread. Like so:
dispatch_async (dispatch_get_main_queue (), ^{
self.imageView2.image = nil;
self.imageView.image = nil;
});