I am capturing video in preview mode and would like to display a still image captured by the camera.
I currently save the image and capture output to ivars defined in the interface as:
UIImage *snapshot
AVCaptureStillImageOutput* stillImageOutput;
The video displays fine. However, when I try to capture and display a still image, nothing is appearing and, in fact, the debugger shows the stillImageOutput and image are nil. I think this may be a timing issue with the asynchronous capture and that I need to use a completion handler, but I am weak on completion handlers.
What is the proper way to display a still image immediately after capturing it without tying up UI:
Code to capture still:
- (void)takeSnapshot {
AVCaptureConnection *videoConnection = nil;
for (AVCaptureConnection *connection in stillImageOutput.connections) {
for (AVCaptureInputPort *port in [connection inputPorts]) {
if ([[port mediaType] isEqual:AVMediaTypeVideo]) {
videoConnection = connection;
break;
}
}
if (videoConnection) {
break;
}
}
[stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection
completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
if (imageDataSampleBuffer != NULL) {
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
snapshot = [UIImage imageWithData:imageData];
}
}];
}
Code to display still. Note absence of completion handler which may be issue, however, I'm not sure how to write that...
[self takeSnapshot];
self.imageView.image = snapshot;
I would change the takeSnapshot method to take in a completion block and then call that completion block within the completion block of your other async method:
captureStillImageAsynchronouslyFromConnection:completionHandler
Here's an example of a method taking a completion block and then calling back to it in the completion block of a method called internally:
// this correlates to your takeSnapshot method
// you want to add a completion portion to this method
- (void)doSomethingAsynchronouslyWithCompletion:(void (^)(NSData *completionData))completion {
// call your other async method
[self anotherAsyncMethodWithItsOwnCompletion:^(NSData *completionDataFromSecondMethod) {
if (completionDataFromSecondMethod.length > 0) {
// this is where you would receive the CMSampleBufferRef from the completion handler of captureStillImageAsynchronouslyFromConnection:completionHandler
// and convert it over to to data
// make sure the completion block isn't nil if it's nullable
if (completion) {
// you would want to pass back the NSData imageData in the completion block here
completion(completionDataFromSecondMethod);
}
}
}];
}
// this method would simulate the captureStillImageAsynchronouslyFromConnection:completionHandler: method
- (void)anotherAsyncMethodWithItsOwnCompletion:(void (^)(NSData * completionDataFromSecondMethod))anotherCompletion {
// this is just to simulate some time waiting for the asnyc task to complete
// never call sleep in your own code
sleep(3);
if (anotherCompletion) {
// this simulates the fake CFSampleBufferRef passed back by the captureStillImage...
NSData *fakeCompletionData = [#"FakeCompletionString" dataUsingEncoding:NSUTF8StringEncoding];
anotherCompletion(fakeCompletionData);
}
}
And an example of how you would call it:
[self doSomethingAsynchronouslyWithCompletion:^(NSData *completionData) {
if (completionData.length > 0) {
// come back on the main queue to modify any UI Elements
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
// this is where you want want to set your self.imageView.image
// self.imageView.image = [UIImage imageWithData:{{dataFromCompletion}}]
NSLog(#"The completionString result = %#", [[NSString alloc] initWithData:completionData encoding:NSUTF8StringEncoding]);
}];
}
}];
This link may be helpful for getting you started with block syntax: http://goshdarnblocksyntax.com
Related
I've got an API connector that downloads images asynchronously from an API (basically a smart wrapper around SDWebImageManager). This serves my purpose perfectly, except in one rare instance I need to use the images as in an image slideshow datasource delegate that expects a UIImage to be returned:
- (UIImage*)slideShow:(KASlideShow *)slideShow imageForPosition:(KASlideShowPosition)position
{
return [self randomImageFromConfig];
}
So I'm trying to use a semaphore to wrap my block call so that I can ensure the image is returned before the slideshow datasource method completes:
- (UIImage*)randomImageFromConfig
{
__block UIImage* returnImage;
NSMutableArray* slideShowImages = [[kGlobalRemoteConfig valueForKeyPath:#"images.home"] mutableCopy];
if (slideShowImages && slideShowImages.count > 0)
{
[slideShowImages shuffle];
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[ImageDownloader imageForId:slideShowImages[0] collection:#"images" operation:nil completion:^(UIImage *image, NSError *error) {
returnImage = image;
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
}
return returnImage;
}
But this appears to be blocking the main thread? Is this somehow related to my underlying method calling SDWebImage? Or am I just being silly? How can I get this block operation to work? Or is there a better way to go about this datasource?
I am using generateCGImagesAsynchronouslyForTimes to make some images and save them to a NSMutableArray, now when the function generateCGImagesAsynchronouslyForTimes finishes I want to use the image in this array, how can I have the code I want to exectue after all the images have been generated to finish. I would just put it in the completionHandler code block, but I don't want it run multiple times I just want to run it once, after this method has finished.
EDIT
This is all inside - (BFTask *)createImage:(NSInteger)someParameter {
AVAssetImageGenerator *imageGenerator = [AVAssetImageGenerator assetImageGeneratorWithAsset:passsedAsset];
[imageGenerator generateCGImagesAsynchronouslyForTimes:times
completionHandler:^(CMTime requestedTime, CGImageRef image, CMTime actualTime,
AVAssetImageGeneratorResult result, NSError *error) {
if (result == AVAssetImageGeneratorSucceeded) {
UIImage *img = [UIImage imageWithCGImage:image];
NSData *imgData = UIImageJPEGRepresentation(img, 1.0);
UIImage *saveImage = [[UIImage alloc] initWithData:imgData];
[mutaleArray addObject:saveImage];
//I get Assigment to read only property error on line below
completionSource.task = saveImage;
}
]};
What should i be assigning that to?
The two approaches I would consider first are NSOperationQueue (you can detect when it's empty) or the easier choice of using the Bolts framework.
Bolts allows you to create an array of tasks that all run asynchronously and then once they're finished it goes on to the next bit.
Let me get a link...
Here you go... https://github.com/BoltsFramework
You can also get this through cocoapods which makes everything much easier.
An example of how bolts works...
At the moment you will have a function that creates an image asynchronously. Something like... - (UIImage *)createImage: (id)someParameter; well now you can do this...
- (BFTask *)createImage:(NSInteger)someParameter
{
BFTaskCompletionSource *completionSource = [BFTaskCompletionSource taskCompletionSource];
//create your image asynchronously and then set the result of the task
someAsyncMethodToCreateYourImageWithACompletionBlock...^(UIImage *createdImage){
// add the images here...
[self.imageArray addObject:createdImage];
// the result doesn't need to be the image it just informs
// that this one task is complete.
completionSource.result = createdImage;
}
return completionSource.task;
}
Now you have to run the tasks in parallel...
- (void)createAllTheImagesAsyncAndThenDoSomething
{
// create the empty image array here
self.imageArray = [NSMutableArray array];
NSMutableArray *tasks = [NSMutableArray array];
for (NSInteger i=0 ; i<100 ; ++i) {
// Start this creation immediately and add its task to the list.
[tasks addObject:[self createImage:i]];
}
// Return a new task that will be marked as completed when all of the created images are finished.
[[BFTask taskForCompletionOfAllTasks:tasks] continueWithBlock:^id(BFTask *task){
// this code will only run once all the images are created.
// in here self.imageArray is populated with all the images.
}
}
Assuming that generateCGImagesAsynchronouslyForTimes:completionHandler: calls its completion handlers sequentially (that seems reasonable, but the docs don't explicitly promise), then this is very simple. Just set a __block variable to the count of your times and decrement it once per completion. When it's zero, call your other function.
__block NSInteger count = [times count];
[imageGenerator generateCGImagesAsynchronouslyForTimes:times
completionHandler:^(CMTime requestedTime, CGImageRef image, CMTime actualTime,
AVAssetImageGeneratorResult result, NSError *error) {
... Do all the stuff ...
if (--count <= 0) {
finalize()
}
If generateCGImagesAsynchronouslyForTimes: actually does work in parallel and so might call the completion handlers in parallel, then you can handle all of this with dispatch groups.
dispatch_group_t group = dispatch_group_create();
//
// Enter the group once for each time
//
[times enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
dispatch_group_enter(group);
}];
//
// This local variable will be captured, so you don't need a property for it.
//
NSMutableArray *results = [NSMutableArray new];
//
// Register a block to fire when it's all done
//
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(#"Whatever you want to do when everything is done.");
NSLog(#"results is captured by this: %#", results);
});
AVAssetImageGenerator *imageGenerator = [AVAssetImageGenerator assetImageGeneratorWithAsset:nil];
[imageGenerator generateCGImagesAsynchronouslyForTimes:times
completionHandler:^(CMTime requestedTime, CGImageRef image, CMTime actualTime,
AVAssetImageGeneratorResult result, NSError *error)
{
if (result == AVAssetImageGeneratorSucceeded) {
//
// Create saveImage
//
id saveImage = #"";
//
// Update external things on a serial queue.
// You may use your own serial queue if you like.
//
dispatch_sync(dispatch_get_main_queue(), ^{
[results addObject:saveImage];
});
//
// Signal we're done
//
dispatch_group_leave(group);
}
}];
downloadImages is a button and whenever I press on it, a spinner should start rolling, an async request should ping Google (to make sure there is a connection) and after a response is received, I start to synchronically downloading images.
Somehow the spinner won't go and it seems as if the request is sync and not async.
- (IBAction)downloadImages:(id)sender {
NSString *ping=#"http://www.google.com/";
GlobalVars *globals = [GlobalVars sharedInstance];
[self startSpinner:#"Please Wait."];
NSURL *url = [[NSURL alloc] initWithString:ping];
NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:5.0];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (data) {
for(int i=globals.farmerList.count-1; i>=0;i--)
{
//Definitions
NSString * documentsDirectoryPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
//Get Image From URL
NSString *urlString = [NSString stringWithFormat:#"https://myurl.com/%#",[[globals.farmerList objectAtIndex:i] objectForKey:#"Image"]];
UIImage * imageFromURL = [self getImageFromURL:urlString];
//Save Image to Directory
[self saveImage:imageFromURL withFileName:[[globals.farmerList objectAtIndex:i] objectForKey:#"Image"] ofType:#"jpg" inDirectory:documentsDirectoryPath];
}
[self stopSpinner];
}
}];
}
The spinner code:
//show loading activity.
- (void)startSpinner:(NSString *)message {
// Purchasing Spinner.
if (!connectingAlerts) {
connectingAlerts = [[UIAlertView alloc] initWithTitle:NSLocalizedString(message,#"")
message:nil
delegate:self
cancelButtonTitle:nil
otherButtonTitles:nil];
connectingAlerts.tag = (NSUInteger)300;
[connectingAlerts show];
UIActivityIndicatorView *connectingIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
connectingIndicator.frame = CGRectMake(139.0f-18.0f,50.0f,37.0f,37.0f);
[connectingAlerts addSubview:connectingIndicator];
[connectingIndicator startAnimating];
}
}
//hide loading activity.
- (void)stopSpinner {
if (connectingAlerts) {
[connectingAlerts dismissWithClickedButtonIndex:0 animated:YES];
connectingAlerts = nil;
}
// [self performSelector:#selector(showBadNews:) withObject:error afterDelay:0.1];
}
As asked: the getImageFromURL code
-(UIImage *) getImageFromURL:(NSString *)fileURL {
UIImage * result;
NSData * data = [NSData dataWithContentsOfURL:[NSURL URLWithString:fileURL]];
result = [UIImage imageWithData:data];
return result;
}
-(void) saveImage:(UIImage *)image withFileName:(NSString *)imageName ofType:(NSString *)extension inDirectory:(NSString *)directoryPath {
if ([[extension lowercaseString] isEqualToString:#"png"]) {
[UIImagePNGRepresentation(image) writeToFile:[directoryPath stringByAppendingPathComponent:[NSString stringWithFormat:#"%#.%#", imageName, #"png"]] options:NSAtomicWrite error:nil];
} else if ([[extension lowercaseString] isEqualToString:#"jpg"] || [[extension lowercaseString] isEqualToString:#"jpeg"]) {
[UIImageJPEGRepresentation(image, 1.0) writeToFile:[directoryPath stringByAppendingPathComponent:[NSString stringWithFormat:#"%#.%#", imageName, #"jpg"]] options:NSAtomicWrite error:nil];
} else {
NSLog(#"Image Save Failed\nExtension: (%#) is not recognized, use (PNG/JPG)", extension);
}
}
That's because you're creating an asynchronous operation and then telling it to execute on the main thread by using [NSOperationQueue mainQueue];.
Instead, create a new instance of NSOpeartionQueue and pass that as the parameter.
NSOperationQueue *myQueue = [[NSOperationQueue alloc] init];
This is an asynchronous problem. Asynchronism is infectious. That means, if any small part of the problem is asynchronous, the whole problem becomes asynchronous.
That is, your button action would invoke an asynchronous method like this (and itself becomes "asynchronous" as well):
- (IBAction)downloadImages:(id)sender
{
self.downloadImagesButton.enabled = NO;
[self asyncLoadAndSaveImagesWithURLs:self.urls completion:^(id result, NSError* error){
if (error != nil) {
NSLog(#"Error: %#", error);
}
dispatch_async(dispatch_get_main_queue(), ^{
self.downloadImagesButton.enabled = YES;
};
}];
}
So, your asynchronous problem can be described as this:
Given a list of URLs, asynchronously load each URL and asynchronously save them to disk. When all URLs are loaded and saved, asynchronously notify the call-site by calling a completion handler passing it an array of results (for each download and save operation).
This is your asynchronous method:
typedef void (^completion_t)(id result, NSError* error);
- (void) asyncLoadAndSaveImagesWithURLs:(NSArray*)urls
completion:(completion_t) completionHandler;
Asynchronous problems can be solved properly only by finding a suitable asynchronous pattern. This involves to asynchronize every part of the problem.
Lets start with your getImageFromURL method. Loading a remote resource is inherently asynchronous, so your wrapper method ultimately will be asynchronous as well:
typedef void (^completion_t)(id result, NSError* error);
- (void) loadImageWithURL:(NSURL*)url completion:(completion_t)completionHandler;
I leave it undefined how that method will be eventually implemented. You may use NSURLConnection's asynchronous convenient class method, a third party helper tool or your own HTTPRequestOperation class. It doesn't matter but it MUST be asynchronous for achieving a sane approach.
Purposefully, you can and should make your saveImage method asynchronous as well. The reason for making this asynchronous is, that this method possibly will get invoked concurrently, and we should *serialize* disk bound (I/O bound) tasks. This improves utilization of system resources and also makes your approach a friendly system citizen.
Here is the asynchronized version:
typedef void (^completion_t)(id result, NSError* error);
-(void) saveImage:(UIImage *)image fileName:(NSString *)fileName ofType:(NSString *)extension
inDirectory:(NSString *)directoryPath
completion:(completion_t)completionHandler;
In order to serialize disk access, we can use a dedicated queue disk_queue where we assume it has been properly initialized as a serial queue by self:
-(void) saveImage:(UIImage *)image fileName:(NSString *)fileName ofType:(NSString *)extension
inDirectory:(NSString *)directoryPath
completion:(completion_t)completionHandler
{
dispatch_async(self.disk_queue, ^{
// save the image
...
if (completionHandler) {
completionHandler(result, nil);
}
});
}
Now, we can define an asynchronous wrapper which loads and saves the image:
typedef void (^completion_t)(id result, NSError* error);
- (void) loadAndSaveImageWithURL:(NSURL*)url completion:(completion_t)completionHandler
{
[self loadImageWithURL:url completion:^(id image, NSError*error) {
if (image) {
[self saveImage:image fileName:fileName ofType:type inDirectory:directory completion:^(id result, NSError* error){
if (result) {
if (completionHandler) {
completionHandler(result, nil);
}
}
else {
DebugLog(#"Error: %#", error);
if (completionHandler) {
completionHandler(nil, error);
}
}
}];
}
else {
if (completionHandler) {
completionHandler(nil, error);
}
}
}];
}
This loadAndSaveImageWithURL method actually performs a "continuation" of two asynchronous tasks:
First, asynchronously load the image.
THEN, if that was successful, asynchronously save the image.
It's important to notice that these two asynchronous tasks are sequentially processed.
Up until here, this all should be quite comprehensive and be straight forward. The tricky part follows now where we try to invoke a number of asynchronous tasks in an asynchronous manner.
Asynchronous Loop
Suppose, we have a list of URLs. Each URL shall be loaded asynchronously, and when all URLs are loaded we want the call-site to be notified.
The traditional for loop is not that appropriate for accomplishing this. But imagine we would have a Category for a NSArray with a method like this:
Category for NSArray
- (void) forEachApplyTask:(task_t)transform completion:(completion_t)completionHandler;
This basically reads: for each object in the array, apply the asynchronous task transform and when all objects have been "transformed" return a list of the transformed objects.
Note: this method is asynchronous!
With the appropriate "transform" function, we can "translate" this to your specific problem:
For each URL in the array, apply the asynchronous task loadAndSaveImageWithURL and when all URLS have been loaded and saved return a list of the results.
The actual implementation of the forEachApplyTask:completion: may appear a bit tricky and for brevity I don't want to post the complete source here. A viable approach requires about 40 lines of code.
I'll provide an example implementation later (on Gist), but lets explain how this method can be used:
The task_t is a "block" which takes one input parameter (the URL) and returns a result.
Since everything must be treated asynchronously, this block is asynchronous as well, and the eventual result will be provided via a completion block:
typedef void (^completion_t)(id result, NSError* error);
typedef void (^task_t)(id input, completion_t completionHandler);
The completion handler may be defined as follows:
If the tasks succeeds, parameter error equals nil. Otherwise, parameter error is an NSError object. That is, a valid result may also be nil.
We can quite easily wrap our method loadAndSaveImageWithURL:completion: and create a block:
task_t task = ^(id input, completion_t completionHandler) {
[self loadAndSaveImageWithURL:input completion:completionHandler];
};
Given an array of URLs:
self.urls = ...;
your button action can be implemented as follows:
- (IBAction)downloadImages:(id)sender
{
self.downloadImagesButton.enabled = NO;
task_t task = ^(id input, completion_t completionHandler) {
[self loadAndSaveImageWithURL:input completion:completionHandler];
};
[self.urls forEachApplyTask:task ^(id results, NSError*error){
self.downloadImagesButton.enabled = YES;
if (error == nil) {
... // do something
}
else {
// handle error
}
}];
}
Again, notice that method forEachApplyTask:completion: is an asynchronous method, which returns immediately. The call-site will be notified via the completion handler.
The downloadImages method is asynchronous as well, there is no completion handler though. This method disables the button when it starts and enables it again when the asynchronous operation has been completed.
The implementation of this forEachApplyTask method can be found here: (https://gist.github.com/couchdeveloper/6155227).
From your code what I can understand is its not due to assyncronous call to load url. but the following code may heavy.
For assynchronous image loading try https://github.com/rs/SDWebImage
//Get Image From URL
NSString *urlString = [NSString stringWithFormat:#"https://myurl.com/%#",[[globals.farmerList objectAtIndex:i] objectForKey:#"Image"]];
UIImage * imageFromURL = [self getImageFromURL:urlString];
//Save Image to Directory
[self saveImage:imageFromURL withFileName:[[globals.farmerList objectAtIndex:i] objectForKey:#"Image"] ofType:#"jpg" inDirectory:documentsDirectoryPath];
Happy coding :)
I have a fairly lengthy method for a stop motion app that is slightly different for each of the various options pressed, timers, self timers, etc
Can define the main body of the method:
// initiate a still image capture, return immediately
// the completionHandler is called when a sample buffer has been captured
AVCaptureConnection *stillImageConnection = [stillImageOutput connectionWithMediaType:AVMediaTypeVideo];
[stillImageOutput captureStillImageAsynchronouslyFromConnection:stillImageConnection
completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *__strong error) {
// set up the AVAssetWriter using the format description from the first sample buffer captured
if ( !assetWriter ) {
outputURL = [NSURL fileURLWithPath:[NSString stringWithFormat:#"%#/%llu.mov", NSTemporaryDirectory(), mach_absolute_time()]];
//NSLog(#"Writing movie to \"%#\"", outputURL);
CMFormatDescriptionRef formatDescription = CMSampleBufferGetFormatDescription(imageDataSampleBuffer);
if ( NO == [self setupAssetWriterForURL:outputURL formatDescription:formatDescription] )
return;
}
// re-time the sample buffer - in this sample frameDuration is set to 5 fps
CMSampleTimingInfo timingInfo = kCMTimingInfoInvalid;
timingInfo.duration = frameDuration;
timingInfo.presentationTimeStamp = nextPTS;
CMSampleBufferRef sbufWithNewTiming = NULL;
OSStatus err = CMSampleBufferCreateCopyWithNewTiming(kCFAllocatorDefault,
imageDataSampleBuffer,
1, // numSampleTimingEntries
&timingInfo,
&sbufWithNewTiming);
if (err)
return;
// append the sample buffer if we can and increment presnetation time
if ( [assetWriterInput isReadyForMoreMediaData] ) {
if ([assetWriterInput appendSampleBuffer:sbufWithNewTiming]) {
nextPTS = CMTimeAdd(frameDuration, nextPTS);
}
else {
NSError *error = [assetWriter error];
NSLog(#"failed to append sbuf: %#", error);
}
}
// release the copy of the sample buffer we made
CFRelease(sbufWithNewTiming);
}];
and just make variations of the method with the timers etc
First I tried making a singleton but although I got the method called I had other issues with the saving and writing to file. Can I make a MACRO out of a method?
I researched on SO here iOS create macro
Am I on the right track? can i define a method rather than image as in that example
Making a macro out of a method, while possible, is a terrible idea for a variety of reasons.
Why not just make it a class method? You won't have to worry about management of a class instance, and it won't muddy up the global namespace.
I have an "UIImage" return type method named "ComLog". I want to return a Image from this method. In "ComLog" method i use GCD to get the image value from an array. I use the following code, the "NSLog(#"qqqqqqqqqqq %#", exiIco)" print the 'image' value but NSLog(#"qqqqqqqqqqq %#", exiIco);" don't.
Here is the details :
-(UIImage*) ComLog
{
ExibitorInfo *currentExibitor100 = [[ExibitorInfo alloc] init];
currentExibitor100 = [self.exibitorsArray objectAtIndex:0];
imageQueueCompanyLogo = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(imageQueueCompanyLogo, ^
{
UIImage *imageCompanyLogo = [UIImage imageWithData:[NSData dataWithContentsOfURL: [NSURL URLWithString:[currentExibitor100 companyLogoURL]]]];
dispatch_async(dispatch_get_main_queue(), ^
{
self.exibitorIcoImageView.image = imageCompanyLogo;
exiIco = imageCompanyLogo;
NSLog(#"qqqqqqqqqqq %#", exiIco);
});
});
return exiIco;
}
- (void)viewDidLoad
{
[super viewDidLoad];
UIImage *a = [self ComLog];
NSLog(#"It should be a image %#", a);
}
Here all the properties are declared globally(In "Myclass.h" file). I am new in Objective C. Please give reply if you know the answer.
Thanks in Advance.
There's so much wrong in your code snippet that it is difficult to decide where to start.
I would suggest to leave GCD for now, and take a look at it later when you are more experienced.
Basically, you want to load an image from a remote server. NSURLConnection provides a convenient method for this which is sufficient for very simple use cases:
+ (void)sendAsynchronousRequest:(NSURLRequest *)request queue:(NSOperationQueue *)queue completionHandler:(void (^)(NSURLResponse*, NSData*, NSError*))handler;
You can find the docs here: NSURLConnection Class Reference.
The recommended approach to load remote resources is using NSURLConnection in asynchronous mode implementing the delegates. You can find more info here:
URL Loading System Programming Guide - Using NSURL Connection
I would also recommend to read Conventions.
Here is a short example how to use sendAsynchronousRequest:
NSURL* url = [NSURL URLWithString:[currentExibitor100 companyLogoURL]];
NSMutableURLRequest* urlRequest = [NSURLRequest requestWithURL:url];
NSOperationQueue* queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:urlRequest
queue:queue
completionHandler:^(NSURLResponse* response,
NSData* data,
NSError* error)
{
if (data) {
// check status code, and optionally MIME type
if ( [(NSHTTPURLResponse*)(response) statusCode] == 200 /* OK */) {
UIImage* image = [UIImage imageWithData:data];
if (image) {
dispatch_async(dispatch_get_main_queue(), ^{
self.exibitorIcoImageView.image = image;
});
} else {
NSError* err = [NSError errorWithDomain: ...];
[self handleError:err]; // execute on main thread!
}
}
else {
// status code indicates error, or didn't receive type of data requested
NSError* err = [NSError errorWithDomain:...];
[self handleError:err]; // execute on main thread!
}
}
else {
// request failed - error contains info about the failure
[self handleError:error]; // execute on main thread!
}
}];
First of all, I would recommend you to read about blocks in Objective C. The dispatch_async block you are using inside your function is async and thus it returns immediately after you use it, as it runs in it's own pool. For proper use, you can call another method to return the image processes inside the block, or post NSNotification when your image is ready. like this:
-(void) ComLog
{
ExibitorInfo *currentExibitor100 = [[ExibitorInfo alloc] init];
currentExibitor100 = [self.exibitorsArray objectAtIndex:0];
imageQueueCompanyLogo = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(imageQueueCompanyLogo, ^
{
UIImage *imageCompanyLogo = [UIImage imageWithData:[NSData dataWithContentsOfURL: [NSURL URLWithString:[currentExibitor100 companyLogoURL]]]];
dispatch_async(dispatch_get_main_queue(), ^
{
self.exibitorIcoImageView.image = imageCompanyLogo;
exiIco = imageCompanyLogo;
NSLog(#"qqqqqqqqqqq %#", exiIco);
[self imageIsReady:exiIco];
});
});
// return exiIco;
}
- (void)imageIsReady:(uiimage *)image
{
//do whatever you want with the image
NSLog(#"Image is here %#", image);
}