NSthreading but still not working with activityprogressview - iphone

[progressind startAnimating];
[self performSelectorOnMainThread:#selector(methodgoeshere:) withObject:[NSDictionary dictionaryWithObjectsAndKeys: aURL, #"aURL", aURL2, #"aURL2", nil]
waitUntilDone:YES ];
[progressind stopAnimating];
[navigationController pushViewController:vFullscreen animated:YES];
In the methodgoeshere, i have a UIImageview which i fill with the image once its downloaded but the problem is, the activityprogress is not working as i thought, it doesnt spin.

You need to do your image loading in a new background thread. performSelectorOnMainThread calls the method on the main thread, not a background thread so that's why it's blocking.
To call your image loading method in a background thread:
[progressind startAnimating];
[NSThread detachNewThreadSelector:#selector(methodgoeshere:) toTarget:self withObject:[NSDictionary dictionaryWithObjectsAndKeys: aURL, #"aURL", aURL2, #"aURL2", nil]];
Then in methodgoeshere you load your image. Once the image is done loading then you call a method on the main thread to let it know you're done and to stop the activity progress indicator.
- (void) methodgoeshere:(...)... {
// <Load your image here>
// Then tell the main thread that you're done
[self performSelectorOnMainThread:#selector(imageDoneLoading) withObject:nil waitUntilDone:YES ];
}
- (void)imageDoneLoading {
[progressind stopAnimating];
// Do other stuff now that the image is loaded...
}

Related

ios setHidden:NO at begin of a long method does not show on display

I have a method, which loads data from a Webservice.
At the start of that method I like to show a UIActivityIndicator.
But only to change the hidden to NO does not redraw the UIView.
So the UIActivityIndicator is visible after the long load of data, which is definitely to late.
So how can I tell the UIView to redraw the activityIndicator before the load of the data begins?
- (IBAction)loadData{
[activity setHidden:NO];
// here the activtyIndicator should appear
...
loadlongdataFromNet..
[activity setHidden:YES];
// here the activtyIndicator should disappear
}
Never perform networking and other computationally intensive operations on the main thread!!!
It will freeze/block the UI. You should move the long network-related processing to a background thread:
- (void)loadData
{
[activity startAnimating]; // use this instead of setHidden
[NSThread detachNewThreadSelector:#selector(reallyLoadData) toTarget:self withObject:nil];
}
- (void)reallyLoadData
{
// network-heavy processing
// then:
[activity performSelectorOnMainThread:#selector(stopAnimating) withObject:nil waitUntilDone:YES];
}
One approach is to use GCD to do this properly:
- (void)loadData {
[activity startAnimating];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
// do long running data load here
dispatch_async(dispatch_get_main_queue(), ^{
[activity stopAnimating];
});
});
}

User Interface commands skipped in IBAction

Here is my code:
-(IBAction)saveDownloadedImage
{
NSLog(#"Test"); EXECUTED
indicatorView.hidden = NO; NOT EXECUTED
[indicatorView startAnimating]; NOT EXECUTED
[statusLabel setText:#"WHY?"]; NOT EXECUTED
[currentPicture setImage:[imageView image]]; EXECUTED
ImageFileManager *fileManager = [[ImageFileManager alloc] init]; EXECUTED
[fileManager saveImageToDisk:currentPicture]; EXECUTED
indicatorView.hidden = YES;
[statusLabel setText:#"Image saved successfully."]; EXECUTED
saveButton.enabled = NO; EXECUTED
}
The proccess of saving takes about 5 seconds. So it would be normal to see the indicator in the UI. But nothing happens! Any idea?
Everything is executed. Your problem is that the saveImageToDisk call is synchronous and you are calling it from the UI thread. When you are blocking the UI thread, nothing is ever repainted. The indicator is shown but it cannot be drawn to the screen until the IBAction returns when it will have been hidden again.
You have to call the saving method asynchronously.
Blocking UI thread is never a good idea.
Edit: see the answer for the following question for the correct solution: asynchronous calls to database in ios
Edit2: One of the possible solutions (not tested)
-(IBAction)saveDownloadedImage {
indicatorView.hidden = NO; //Note you can use hidesWhenStopped property for this
[indicatorView startAnimating];
[statusLabel setText:#"BECAUSE..."];
[currentPicture setImage:[imageView image]];
[NSThread detachNewThreadSelector:#selector(save) toTarget:self withObject:nil]
}
- (void)save {
#autoreleasepool {
ImageFileManager *fileManager = [[ImageFileManager alloc] init];
[fileManager saveImageToDisk:currentPicture];
[self performSelectorOnMainThread:#selector(updateUI) withObject:nil waitUntilDone:NO];
}
}
- (void)updateUI {
indicatorView.hidden = YES;
[statusLabel setText:#"Image saved successfully."];
saveButton.enabled = NO;
}
Are you sure that
1) indicatorView and statusLabel are not null, and
2) indicatorView and statusLabel are added as subviews to self.view?
In My Perception your are starting the Activity Indicator main Thread.
Instead Showing the Indicator on main thread
you should call Indicator on seperate Thread as Below.
-(IBAction)saveDownloadedImage
{
NSLog(#"Test"); EXECUTED
commented below code
//indicatorView.hidden = NO; NOT EXECUTED
// [indicatorView startAnimating]; NOT EXECUTED
//instead of main thread create new Thread as Below
[NSThread detachNewThreadSelector:#selector(showloader) toTarget:self withObject:nil];
[statusLabel setText:#"WHY?"]; NOT EXECUTED
[currentPicture setImage:[imageView image]]; EXECUTED
ImageFileManager *fileManager = [[ImageFileManager alloc] init]; EXECUTED
[fileManager saveImageToDisk:currentPicture]; EXECUTED
[statusLabel setText:#"Image saved successfully."]; EXECUTED
saveButton.enabled = NO;
[indicatorView stopAnimating:YES];
indicatorView.hidden = YES;
}
//Custome method For shoing the Indicator.
-(void)showloader{
//call below method here indicatorView object created already.
[indicatorView startAnimating]
}
It'll definitely work
You need to declare your method with a sender like this
-(IBAction)saveDownloadedImage:(id)sender

How do I run a process without blocking user interface in my iphone app

I am accessing the photo library on the iphone and it takes a long time to import the pictures i select in my application, how do i run the process on a secondary thread , or what solution do i use to not block the user interface?
I did a full explanation with sample code using performSelectOnBackground or GCD here:
GCD, Threads, Program Flow and UI Updating
Here's the sample code portion of that post (minus his specific problems:
performSelectorInBackground Sample:
In this snippet, I have a button which invokes the long running work, a status label, and I added a slider to show I can move the slider while the bg work is done.
// on click of button
- (IBAction)doWork:(id)sender
{
[[self feedbackLabel] setText:#"Working ..."];
[[self doWorkButton] setEnabled:NO];
[self performSelectorInBackground:#selector(performLongRunningWork:) withObject:nil];
}
- (void)performLongRunningWork:(id)obj
{
// simulate 5 seconds of work
// I added a slider to the form - I can slide it back and forth during the 5 sec.
sleep(5);
[self performSelectorOnMainThread:#selector(workDone:) withObject:nil waitUntilDone:YES];
}
- (void)workDone:(id)obj
{
[[self feedbackLabel] setText:#"Done ..."];
[[self doWorkButton] setEnabled:YES];
}
GCD Sample:
// on click of button
- (IBAction)doWork:(id)sender
{
[[self feedbackLabel] setText:#"Working ..."];
[[self doWorkButton] setEnabled:NO];
// async queue for bg work
// main queue for updating ui on main thread
dispatch_queue_t queue = dispatch_queue_create("com.sample", 0);
dispatch_queue_t main = dispatch_get_main_queue();
// do the long running work in bg async queue
// within that, call to update UI on main thread.
dispatch_async(queue,
^{
[self performLongRunningWork];
dispatch_async(main, ^{ [self workDone]; });
});
}
- (void)performLongRunningWork
{
// simulate 5 seconds of work
// I added a slider to the form - I can slide it back and forth during the 5 sec.
sleep(5);
}
- (void)workDone
{
[[self feedbackLabel] setText:#"Done ..."];
[[self doWorkButton] setEnabled:YES];
}
Use an asynchronous connection. It won't block the UI while it does the fetching behind.
THIS helped me a lot when I had to do download images, lot of them.
One option is use performSelectorInBackground:withObject:

my progressView is not refreshing

When my app starts for the first time, I have a background process which runs off of a thread and the user should see a progressView which should show the progress being made. The code below shows how I am setting up my thread and progressView
//from viewDidLoad
progView.hidden = NO;
progView.progress = 0.0;
[NSThread detachNewThreadSelector:#selector(buildTable) toTarget:self withObject:nil];
-(void)buildTable
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[self performSelectorOnMainThread:#selector(addData) withObject:nil waitUntilDone:NO];
[pool release];
}
the function addData is where I update the progressView using the following code -
progView.progress = 0.1
However, the progressView is visible but even though the background process is updating the progressView using the above code it is not appearing so on the screen. Do I need to use some form of StartAnimating while the background process is running?
You should only update user interface items from the main thread. You're probably not seeing any change to the progress indicator because you're trying to change it from a background thread.
A very simple strategy to do this is to call performSelectorOnMainThread from your background thread to call a simple method that updates the progress bar on the main thread.
For example, in your addData method (in the background thread) you can call:
[self performSelectorOnMainThread:#selector(updateProgressBar:) withObject:[NSNumber numberWithFloat:newProgressValue] waitUntilDone:false];
And then in your updateProgressBar method (which will run on the main thread), do the progress bar update using the given data:
- (void)updateProgressBar:(NSNumber *)progressValue {
progView.progress = [progressValue floatValue];
}

view hierarchy refresh timing

I'm trying to add a progress meter, or other "I'm busy right now" notification to my view hierarchy right before doing some intense computation that will block the UI. My code looks some thing like:
//create view
[currentTopView addSubView:imBusyView];
//some initialization for the intense computation
[computation startComputing];
Unfortunately, my progress meter doesn't display until after the computation completes. It appears like the views aren't re-drawn until the run loop completes. I'm pretty sure that setNeedsDisplay and setNeedsLayout will also wait until the run loop completes.
How do I get the view to display immediately?
Redrawing only occurs when your code returns control to the run loop. So the easiest way would be for you to schedule the startComputing call with a zero delay. That way, it will be executed during the next run loop iteration (right after redrawing):
[computation performSelector:#selector(startComputing) withObject:nil afterDelay:0.0];
Be aware, though, that unless you do your computation in another thread you will not be able to update the UI during the computation.
If you are doing heavy calculations maybe spawning a new thread is a good idea.
Here I have an activityIndicator displayed and starts a large XMLParse operation in a background thread:
- (void) setSearchParser {
activityIndicator = [[ActivityIndicatorView alloc] initWithActivity];
[self.view addSubview:activityIndicator];
[NSThread detachNewThreadSelector:#selector(getSearchResults:) toTarget:self withObject:[searchParser retain]];
}
then the getSearchResults method:
- (void) getSearchResults: (SearchResultParser *) parser {
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
[parser startParser];
[self performSelectorOnMainThread:#selector(searchResultsReady:) withObject:[parser data] waitUntilDone:NO];
[pool release];
}
So firstly make a new thread:
[NSThread detachNewThreadSelector:#selector(getSearchResults:) toTarget:self withObject:[searchParser retain]];
this means that all code inside the getSearchResults will be executed on a different thread. getSearchResults also get's passed a parameter of "searchParser" thats a large object that just needs startParse called on it to begin.
This is done in getSearchResults. When the [parser startParser] is done, the results is passed back to the main thread method called "searchResultsReady" and the threads autorelease pool is released.
All the time it took from my parser began to it had finished, a gray view covered the screen an an activityIndicator ran.
You can have the small activityIndicator class I wrote:
-(id) initWithActivity {
[self initWithFrame:[self bounds]];
[self setBackgroundColor:[UIColor blackColor]];
[self setAlpha:0.8];
activityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
activityView.center = CGPointMake(160, 240);
[self addSubview:activityView ];
[activityView startAnimating];
return self;
}
- (void) dealloc {
[activityView release];
[super dealloc];
}
Hope it helps you out, even though threads seems a bit confusing, they can help to make the UI not freeze up, which is especially important on the iPhone.