My application has a button when clicked it is disabled, an activity indicator displayed and a background task is executed. When this task is completed a callback updates the interface by enabling the button and removing the activity indicator. The problem I having is the task is completing the callback function is executed but for a period of time the activity monitor remains on the screen, the button looks like it is disabled but it is possible to click it again. Can anyone tell me where I am going wrong?
Thanks very much!
Could it be that the callback-method is being executed in a separate thread?
I'm asking, because any calls that have impact on a view should be performed on the main thread.
The problem might be solved by doing the following:
create a Method that handles your UI-related code and gets called within your callback method
the UI-related code has to be performed on the main thread
It could look a little bit like this:
//gets called asynchronously when your operation has completed
-(void)myCallbackHandler {
[self performSelectorOnMainThread:#selector(updateUI) withObject:nil
waitUntilDone:NO];
}
-(void)updateUI {
[myActivityIndicatorView stopAnimating];
[myButton setEnabled:YES];
}
Related
I know how to animate,display the Activity Indicator.
But I want to know how to immediately show the Activity Indicator.
Now When I am click the button it will load another view after 5 or many seconds. Clicking that button is loading a subview. When that Button Click method is called, it will call more than 7 methods after that and then it will load the subview. But the ActivityIndicator is displayed only after it complete executing all the 7 methods.
What I am trying to do is , I want to display the ActivityIndicator immediately after that Button Click method.
Any Idea ?
-(IBAction)button_click{
..
..
..
[self performSelector:#selector(afterDelay) withObject:nil afterDelay:0.3];
}
-(void)afterDelay{
YOUR_Code
}
The problem is that you perform your massive calculations (or loading from the internet?) on the main thread, but all the view mutation operations are not called immediately — on the start of the runloop the framework creates a new implicit Core Animation transaction, then it collects all the information about views' mutations, then commits the transaction on the end of the runloop. By blocking the main thread you are not allowing the transaction to commit and start your indicator's animation.
You can read about this architecture in the documentation: Core Animation Programming Guide: Transactions.
There are three options:
(Preferred) Perform your operations on the background.
Link your binary against QuartzCore.framework, then #import <QuartzCore/QuartzCore.h> and call [CATransaction commit] after your startAnimating call. This way you commit the implicit transaction.
Create your own explicit CATransaction.
If you have put the code to display or unhide the activityIndicator but it is not shown you have to use performSelector:withObject:afterDelay: method for doing any thing after unhide or displaying the activityIndicator with the delay of 0.001 will show the indicator immediately after clicking the button
Happy Coding :)
If your are talking about the system activity indicator, use this :
[[UIApplication sharedApplication] performSelector:#selector(setNetworkActivityIndicatorVisible:) withObject:[NSNumber numberWithBool:YES ] afterDelay:0.0];
I am trying to show and hide three controls like UIBUtton, UILabel etc. in some scenario. I am doing this using below two functions.
- (void) hide
{
usernameField.hidden=YES;
passwordField.hidden=YES;
myLabel.hidden=YES;
}
- (void) show
{
usernameFieldField.hidden=NO;
passwordField.hidden=NO;
myLabel.hidden=NO;
}
But, when i call these functions under some server response code, that is i'm trying to show under connectionDidFinishLoading in success/failure server response..Its not doing that. i.e. Its not showing or hiding these controls in these situations..But same time, if i call these functions under a button click, its showing/hiding controls..So, Would these functions be called only under some events like button click? Can't we call from anywhere like i'm trying to do?
Please advise!
in your connectionDidFinishLoading
[self performSelectorOnMainThread:#selector(show)
withObject:nil
waitUntilDone:wait];
the reason is UI update show be called on main thread in order to get redraw.
Changing the hidden state of a control will not immediately draw the change to the screen, they will only flag it as needing to be drawn at some point in the future.
If the main thread is busy, then they will not get a chance to draw themselves.
Is your NSURLConnection code running on the main thread? You should move it to a background thread (but beware, changing the hidden property must be done on the main thread, not on a background thread! Look up grand central dispatch.)
When I cancel an NSOperation (when user presses a button) cancel method is called from the main thread, but evidently the operation is running in another thread.
So, to avoid race conditions when I change _isExecuting and _isFinished, I think cancel (or at least its logic) should be called from the same thread that the NSOperation. Apart from that, when user cancels it, several files are deleted and it takes time. Because cancel is called from main thread, all the app becomes unresponsive for a while, which is ugly.
How can I execute cancel code in the same thread that the current NSOperation?
I tried this in cancel (similar to what I saw in ASIHTTPRequest):
if (_operationThread) {
[self performSelector:#selector(cancelOnRequestThread) onThread:_operationThread withObject:nil waitUntilDone:NO];
} else {
[self cancelOnRequestThread];
}
And _operationThread is setted in start method using:
_operationThread=[NSThread currentThread];
But it doesn't work.
Any idea or suggestion?
Note: I use concurrent operations, so I use start instead of main.
Thanks a lot for help.
Ricardo.
It's fine to call cancel on an NSOperation from the main thread. The cancel method is thread-safe.
That shouldn't cause any blocking on your main thread because the cancel method itself shouldn't be doing any work. If you have overridden the cancel method of your operation to delete files, etc then that is the wrong approach. You shouldn't override the cancel method, instead just check the isCancelled method at regular points within the operation's main method (e.g. inside any tight loops) and then return from main early if isCancelled returns YES, which will then cancel the operation on the same thread as the rest of the execution.
If that's how you've implemented it already and you're still having performance issues, is it possible that your operation is not really running on a background thread at all? For example if you've added it to the queue returned by [NSOperationQueue mainQueue] then that's actually running on the main application thread.
I perform this selector in my application:
- (void) doFilter:(UIButton*)button {
[activityIndicator startAnimating];
[self disableButtons];
// ...
// some actions
// ...
sleep(2);
[self enableButtons];
[activityIndicator stopAnimating];
}
when user clicks on button. activityIndicator is UIAtivityIndicatorView. But I don't see any activity indicators while this code is performing. How can I fix it?
Firstly, you should never ever use sleep on the main thread. It blocks your entire app and the user can't do anything for that time.
Secondly, the UI is not updated until your code returns control to the run loop. So whenever you call startAnimating and stopAnimating in the same method without returning to the run loop in between, you can be sure that nothing will happen in the UI (same with disableButtons and enableButtons).
Solution: call startAnimating and disableButtons. Then start the tasks you have to perform in the background so that the UI is not blocked. You can use NSOperation, performSelectorInBackground:..., Grand Central Dispatch etc. for that. Finally, when the long task is finished, have it call another method on the main thread which then calls stopAnimating and enableButtons.
i googled for hours now but i can't find a code example, which describes how to show an uiactivityview while an operation/method is loading.
in my case i want to show it while i load a large image to an uiimageview. have you got an idea how to realize that?
thanks in advance.
sean
You might want to look in the UI UIActivityIndicatorView Class Reference documentation.
Make sure to return to the run loop while waiting for the image to load in order to see the activity indicator animating. Also, you will probably need to create a method to stop the activity indicator, and perhaps call that stop method by doing a performSelectorOnMainThread from whatever method handles the asynchronous completion of the loading.
well its quit easy.
show activity indicator
start thread loading that image
if image loaded, thread tells mainapp to kill activity indicator.
NSThread detachNewThreadSelector:#selector(myMethod)
toTarget:self
withObject:nil];
and
[self performSelectorOnMainThread:#selector(myMethod)
withObject:nil
waitUntilDone:false];
will be helpfully.
taken from http://www.iphoneexamples.com/