Image Loading in Thread Makes Navigation Transition Choppy - iphone

I have an app where a user clicks a photo thumbnail which triggers the next viewcontroller to be pushed on the navigation stack which is responsible for displaying a larger version of the picture. When the photo thumbnail button is pushed, I send the photos alasset reference to the next viewcontroller which loads it in its viewDidLoad method as follows:
dispatch_async(dispatch_get_main_queue(),
^{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
UIImageView *tempMyImageView = [[UIImageView alloc] initWithImage: [UIImage imageWithCGImage:[[[self myAsset] defaultRepresentation] fullScreenImage]]];
[self setMyImageView: tempMyImageView];
[myViewContainer addSubview: [self myImageView]];
[tempMyImageView release];
[pool drain];
});
Everything seems to work OK, except for when I send a large image (like one I took from my camera), the navigation transition is choppy. Does anyone know how to fix this?

You might try just loading the image after the transition is done.
Also of note is that you're not actually doing that in a new thread, as you specified the main queue. Not that that would be a good idea here, as you don't want to execute UI code on another thread.

Related

NSOperation mainQueue issue

I have slideshow, and I want to show Big images, I added to [NSOperation mainQueue] operation with low priority, this operation shows the image.
If image is small , everything is OK, but when image is about 5Mb, the view freeze for 1 second, and I can't scroll my slideshow. I think, that displaying big images just so difficult for iPhone, that main queue is too overloaded.
But I don't inderstand it , because all my displaying code is executed in low priority operation.
Here is the displaying code.
[imageView removeFromSuperview];
imageView = nil;
// reset our zoomScale to 1.0 before doing any further calculations
self.zoomScale = 1.0;
// make a new UIImageView for the new image
self.imageView = [[[UIImageView alloc] initWithImage:image] autorelease];
[self addSubview:imageView];
self.contentSize = [image size];
[self setMaxMinZoomScalesForCurrentBounds];
self.zoomScale = self.minimumZoomScale;
May be I can set the priority for gesture recognizers (the regular questure recognizers for UIScrollView?)
Update
Please look at my new topic, I described the issue more properly my topik
Priority has to do with scheduling. If you queue up a bunch of operations during a runloop iteration then they will be executed by their priority on that queue.
One solution to speed this up would be to either include resources that are scaled to the exact size that you are displaying them in. If you are trying to show a 2000x2000 px image in a 200x200 area then the system to scale all this stuff in memory. You can also dynamically create smaller to fit images programmatically. This can be done on a background queue so your UI is still responsive.
How to resize the image programmatically in objective-c in iphone
If I understand you correclty and you have done something like
NSOperationQueue* queue = [NSOperationQueue mainQueue];
Then any NSOperation you add to it will execute on the main dispatch queue which is concurrent (and responsible for executing tasks on the main thread). That would explain the freeze. You can create your own queue which would start a thread for every NSOperation and would free the main thread to render the UI normally as:
NSOperationQueue* queue = [[NSOperationQueue alloc] init];
[queue addOperation:operation];
This however would cause a problem. When the image is finished loading and you pass it to an UIImageView on the screen there will be a big delay until the Image is actually rendered because the main (UI) thread will not be aware of the action until it chooses to refresh (a few seconds later). The solution to this is to add a 'performInMainThread' message to the end of the main method of the NSOperation as such:
-(void)main {
NSData *bgImageData = [NSData dataWithContentsOfURL:self.targetUrl];
UIImage *img = [UIImage imageWithData:bgImageData];
[self performSelectorOnMainThread:#selector(insertImageLoaded:)
withObject:img
waitUntilDone:YES];
}
The 'insertImageLoaded' is in the NSOperation and would load call a setImage:(UIImage*)img to the component you want.

How to hide an image for few milliseconds while page is loading.

I have a common image on number of pages of my project, so I place that image on window file.
As some pages need not to display that image so i hide that image on corresponding pages.
Now problem arises, as i pop from non- image page to page containing image, that image displays before loading the whole page.
I want that image to appear only when whole page is loaded.
Any help is appreciated....
code of my firstViewController:
ProfileViewController *ProfilePage = [[ProfileViewController alloc] initWithNibName:#"ProfileViewController" bundle:nil];
ProfilePage.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
UINavigationController *navigationControllerNew = [[UINavigationController alloc] initWithRootViewController:ProfilePage];
//Present navigationController as Model viw controller
[self.navigationController presentModalViewController:navigationControllerNew animated:YES];
//release it After presenting to it
[navigationControllerNew release];
[ProfilePage release];
on ProfileViewController page I am hiding image with following code:
[super viewWillAppear:animated];
IQ_TestAppDelegate *appDelegate = (IQ_TestAppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate removeImageViewLogo];
this how I am calling my Pages....
EDIT: I have already used NSTimer, so that image is hidden for some milliseconds, but as i told I placed that image in window file, it displays at the beginning of the page loading, dont know wat to do....
Please help.
You should try and display (make visible) the image in the viewDidLoad method of your controller.
If you have a model that loads some data asynchronously, then you should make the image visible only at the end of this process.
If you provide more details, specifically what you mean by "whole page is loaded", I can help further.

How to get notified about imageWithContentsOfFile: completion?

I load a huge-huge image with imageWithContentsOfFile:, so I have to set up an activityIndicator during the process.
Is there any way/any delegate callback I can use to be informed about the end of this loading process?
imageWithContentsOfFile is synchronous.
You could start an activity indicator, load your big image into memory in a background thread and then go back to the main thread and stop the indicator.
- (void)loadBigImage {
[activityIndicator startAnimating];
[self performSelectorInBackground:#selector(loadBigImageInBackground) withObject:nil];
}
- (void)loadBigImageInBackground {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
UIImage *img = [UIImage imageWithContentsOfFile:#"..."];
[self performSelectorOnMainThread:#selector(bigImageLoaded:) withObject:img waitUntilDone:NO];
[pool release];
}
- (void)bigImageLoaded:(UIImage *)img {
[activityIndicator stopAnimating];
// do stuff
}
Short answer: Nope. sorry!
Long answer :
You could open the file in a background process (an NSOperation?) bit by bit using C style methods i.e. fopen, fread etc) and fire notifications back to the main thread during the load. Then create the image and fire a notification that the image is ready?
If you want to have a delegate & be informed of the progress of the load, you can use an NSURLConnection instead of the synchronous imageWithContentsOfFile.
There's an example of this in the Apple URL Loading System Programming Guide
Your NSURLConnection delegate didReceiveData: method could append the incoming data to an NSData object, then you would use UIImage imageWithData: to create them image once everything's downloaded.
This gives you the most flexibility/control over monitoring the progress of the load; although if all you're trying to do is avoid hanging the UI while the image downloads, simply using imageWithContentsOfFile in a background thread may be easier.

Problem with MBProgressHUD while loading images from server

I have a server that provides a JSON with certain parameters and the name of an image. Then I get the image with the name provided. All this is done in one function called loadingOfImageAndInformation. I use the MBProgressHUD as follows:
[HUD showWhileExecuting:#selector(loadingOfImageAndInformation)
onTarget:self withObject:nil animated:YES];
Which should show the progress thingy when this method is currently running. Now, inside the method I use a ASIHTTPRequest to retrieve all the data I need. Which means that sometime it will jump from that method to the request methods (to retrieve the image and assign it to the UIImageView on the requestLoadDone).
Now, the problem is that the MBProgressHUD thingy only shows until the parameters are shown (the image description I put on a label, the description I got from JSON) and not until the parameters AND image are shown. So basically the loader disappears BEFORE the image is on the imageView.
This is the code in question:
- (void) loadingHudAlert {
NSString *loadingMessage = NSLocalizedString(#"Please Wait",
#"Message displayed when the loading spinner is on");
HUD = [[MBProgressHUD alloc] initWithView:self.view];
HUD.minShowTime = 1.0;
[self.view addSubview:HUD];
HUD.delegate = self;
HUD.labelText = loadingMessage;
[HUD showWhileExecuting:#selector(loadingOfImageAndInformation)
onTarget:self withObject:nil animated:YES];
}
The ASIHTTPRequest does all request on an asynchronous mode. Without a queue.
Thank you for your feedback!!!!
I solved it using a boolean, if the method requestLoadDone is finished the bool turns to be NO and while the boolean is YES the loadingOfImageAndInformation will run (which means will run until the image is correctly retrieved.) I haven't posted this as an answer since maybe someone has a better solution.
So you're saying that the HUD disappears before the image is shown at your imageview, don't you? Maybe this is more due to how cocoa processes this stuff than MBProgressHUD problem.
How are you assigning the image? Have you tried?
[yourimageview setNeedsDisplay];
What I'm trying to say is that, if this is how cocoa manages the imageviews showing stuff, maybe you can try to "force" the reload or displaying to be done before.

How can I display a introduction modal view when the app start up?

I have a tab bar application and I want to display those views that most part of apps have, with the name of the company or the name of the app.
I've created the follow viewController
Introduction *introducao = [[Introduction alloc] initWithNibName:#"Introduction" bundle:nil];
I don't know where exactly should I insert the code to show the modal because I have a tab bar application:
[self.navigationController presentModalViewController:galeria animated:YES];
I've tried to insert these lines on appDelegate.. but didn't work.. somebody have an idea?
if you are trying to show a splash screen right when the application opens, you should use a Default.png image instead of a view controller showing an image. Check out the Apple Documentation on Human Interface Guidelines and Beginning iPhone Development.
First of all, you'll need to ensure that you have a navigation controller present to present the model view from. Otherwise in the above code you'll be messaging nil and nothing will happen. Then you'll want to put the presentModalViewController:animated: call in your app delegate's applicationDidFinishLaunching: implementation.
Thanks for all answers.. they were very useful to understand better the process..
I have found a solution that does exactly what I need! So if someone need to create those splash screens with a sequence of images it is very useful:
Just create a ImageView on the Delegates Header and do the following:
splashView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)];
splashView.image = [UIImage imageNamed:#"Default.png"];
[window addSubview:splashView];
[window bringSubviewToFront:splashView];
to control the duration of the splash screen:
[self performSelector:#selector(removeSplash) withObject:nil afterDelay:1.5];
To remove the splash:
-(void)removeSplash;
{
[splashView removeFromSuperview];
[splashView release];
}
so If you want to create a sequence of image just create a method to change the splashView.image.. and create a NSTIMER to call it..