UIActivityIndicatorView animation issue - objective-c

I am using a UIActivityIndicatorView as an IBOutlet. I am trying to control it using [activityView startAnimating] and [activityView stopAnimating].
I have enabled 'Hides when stopped' and 'Animating' behaviors in XIB file.
I want to start animate the spinner for several user actions inside the controller. But after the first [activityView stopAnimating] call it does not response to [activityView startAnimating] call again.
That is mean the spinner is disappeared after the first [activityView stopAnimating] call.
I tried activeView.hidden = NO; before the next [activityView startAnimating] call. But it does not work.
Any idea about this issue?
Edited
After my controller loaded I do follow thing,
do {
[self.activityIndicator startAnimating];
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
[webServiceCallOperation getResults];
[self.activityIndicator stopAnimating];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
} while (![webServiceCallOperation isValideResponse] || [webServiceCallOperation isServerError]);
In the same controller I have a IBAction for a button click.
- (IBAction)tappedSearchButton:(id)sender
{
[self.activityIndicator startAnimating];
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
[self tappedSearchButtonAction];
[self.activityIndicator stopAnimating];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}
I noticed that the spinner not begin to animate just after the [self.activityIndicator startAnimating]; call. Using [self tappedSearchButtonAction]; I call to a web service and go to the search results view. The spinner begin to animate just before change the view. That's why I did not see it. But I suppose to animate it before web service call and should animate it while my web service call.
Same behavior of the network activity indicator.

As aadhira suggested check for release and if you are making the calls on the main thread.
[NSObject performSelectorOnMainThread:#selector(SEL) withObject:nil waitUntilDone:YES];

It starts animating because as you say you have enabled Animating behavior in the XIB file. But after you stop it for the first time, it is not animating anymore until you start it again programatically.

Related

Why isn't activity indicator working for web view?

I am trying to load a web view (called widget) and set an indicator (called indicator) to show that the view is loading (the web page is no larger than www.google.com). The widget loads fine (I haven't included that code) but this snippet, which should send startAnimating to a UIActivityIndicatorView is not working (no animation or even appearance). Everything is connected in storyboard and "not loading" is always logged. This makes me think there is something wrong with my use of UIWebView's loading property.
sleep(2);
[super viewDidLoad];
[indicator setHidesWhenStopped:YES];
if (widget.loading == YES) {
NSLog(#"loading");
[indicator startAnimating];
} else {
NSLog(#"not loading");
[indicator stopAnimating];
}
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
Any thoughts?
Thanks
Try putting your start and stop animating in the delegate methods for the webview:
- (void)webViewDidFinishLoad:(UIWebView *)webView {
[indicator stopAnimating];
}
- (void)webViewDidStartLoad:(UIWebView *)webView {
[indicator startAnimating];
}
Make sure you set the webview's delegate and are conforming to UIWebViewDelegate in your interface.

MBProgressHUD while logging out

Im using the MBProgressHUD to make an overview loading screen while logging out in my ipad app. That progress takes some time because I have to encrypt some bigger files.
Because Im doing it in a background thread and the MBProgressHUD is animating on the main thread, I had to do something to know when my background thread is finished.
As a test, I did it like that:
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.mode = MBProgressHUDAnimationFade;
hud.labelText = #"Do something...";
[self performSelectorInBackground:#selector(doSomethingElse) withObject:nil];
And method doSomethingElse:
-(void)doSomethingElse
{
[self encrypt];
[self performSelectorOnMainThread:#selector(doSomethingElseDone) withObject:nil waitUntilDone:YES];
}
And method doSomethingElseDone:
-(void)logoutInBackgroundDone
{
[MBProgressHUD hideHUDForView:self.view animated:YES];
}
The solution works but I think there must be a better way? How can I do that on a better way?
Any help is really appreciated.
You can directly dismiss the MBProgressHUD from doSomethingElse method using dispatch_async
-(void)doSomethingElse
{
[self encrypt];
dispatch_async(dispatch_get_main_queue(), ^{
[MBProgressHUD hideHUDForView:self.view animated:YES];
});
}
Create an atomic property you can get to
#property BOOL spinning;
and
- (void)myTask
{
while ( self.spinning )
{
usleep(1000*250); // 1/4 second
}
}
then use something in your view controller like
HUD = [[MBProgressHUD alloc] initWithView:self.view];
[self.view addSubview:HUD];
[HUD showWhileExecuting:#selector(myTask) onTarget:self withObject:nil animated:YES];
This way the HUD will remove itself when spinning becomes false. The spinner has to be atomic since it will be referenced in a background thread. Whatever you are waiting on can simply set the spinner property to false from any thread to indicate it's done.
This is under ARC.

performSegueWithIdentifier not working

I have this code that loads a new UIView (from storyboard) when a button is pressed. goPressed function is fired on button pressed and it calls selectImage function. selectImage opens a UIImagePickerController and lets user select a photo. After user has selected the photo, didFinishPickingMediaWithInfo delegate adds the selected image to a UIImageView.
In 'goPressed', after selectImage is performed, it should be performing a Segue at like commented as //1. But nothing happens. performSegueWithIdentifier doesn't seem to be working. And if I don't call [self selectImage] before calling performSegueWithIdentifier, it works. Here is the code:
- (IBAction)goPressed:(id)sender {
[self selectImage];
[self performSegueWithIdentifier:#"lastView" sender:currentSender]; //1
}
-(void)selectImage
{
// Create image picker controller
UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
// Set source to the camera
imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
// Delegate is self
imagePicker.delegate = (id)self;
// Allow editing of image ?
imagePicker.allowsEditing=NO;
// Show image picker
[self presentModalViewController:imagePicker animated:YES];
}
- (void) imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
// Access the uncropped image from info dictionary
UIImage *image = [info objectForKey:#"UIImagePickerControllerOriginalImage"];
[[picker presentingViewController] dismissModalViewControllerAnimated:YES];
lePreview.image= image;
}
Please help, why performSegueWithIdentifier isn't working? I hope I am not missing any information you need.
I'm assuming that the view you are trying to segue to is using the picture you get just before you do your segue? If they cancel from the image picker do you still want to segue?
If it needs the picture, then maybe you should call your segue after the delegate call "did finish picking".
The issue with the segue not firing may be due to the animation still occurring from here:
[[picker presentingViewController] dismissModalViewControllerAnimated:YES];
You can try:
[[picker presentingViewController] dismissModalViewControllerAnimated:NO];
or if you want to maintain the animation, move the segue to the "picker did finish" method and do it this way:
[self dismissModalViewControllerAnimated:YES completion:^() {
[self performSegueWithIdentifier:#"lastView" sender:self];
}];
or if that does not work try this approach in the pickerdidfinish method (note - this should be implemented as a delegate in the controller that calls the modal view, not the modal view itself:
//maintain the animation
[self dismissModalViewControllerAnimated:YES];
//slight pause to let the modal page dismiss and then start the segue
double delayInSeconds = 0.5;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
//code to be executed on the main queue after delay
[self performSegueWithIdentifier:#"lastView" sender:self];
});
I use this transition frequently and it makes a nice drop away with the modal view then slides in the segue view and the pause allows the transition to seem natural.

How show activity-indicator when press button for upload next view or webview?

when i click on button which title is click here to enlarge then i want show activity indicator on the first view and remove when load this view.
but i go back then it show activity indicator which is shown in this view.
in first vie .m file i have use this code for action.
-(IBAction)btnSelected:(id)sender{
UIButton *button = (UIButton *)sender;
int whichButton = button.tag;
NSLog(#"Current TAG: %i", whichButton);
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
[spinner setCenter:CGPointMake(160,124)];
[self.view addSubview:spinner];
[spinner startAnimating];
if(whichButton==1)
{
[spinner stopAnimating];
first=[[FirstImage alloc]init];
[self.navigationController pushViewController:first animated:YES];
[spinner hidesWhenStopped ];
}}
in above code i have button action in which i call next view. Now i want show/display activity indicator when view upload. In next view i have a image view in which a image i upload i have declare an activity indicator which also not working. How do that?
Toro's suggestion offers a great explanation and solution, but I just wanted to offer up another way of achieving this, as this is how I do it.
As Toro said,
- (void) someFunction
{
[activityIndicator startAnimation];
// do computations ....
[activityIndicator stopAnimation];
}
The above code will not work because you do not give the UI time to update when you include the activityIndicator in your currently running function. So what I and many others do is break it up into a separate thread like so:
- (void) yourMainFunction {
activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
[NSThread detachNewThreadSelector:#selector(threadStartAnimating) toTarget:self withObject:nil];
//Your computations
[activityIndicator stopAnimating];
}
- (void) threadStartAnimating {
[activityIndicator startAnimating];
}
Good luck!
-Karoly
[self.navigationController pushViewController:first animated:YES];
Generally, when you push a view controller into navigation controller, it will invoke the -(void)viewWillAppear: and -(void)viewDidAppear: methods. You can add activity indicator view inside the viewWillAppear: and call startAnimation of indicator view. You CANNOT invoke startAnimation and stopAnimation at the same time. For example,
- (void)viewWillAppear:(BOOL)animated
{
[aIndicatorView startAnimation];
// do somethings ....
[aIndicatorView stopAnimation];
}
Because the startAnimation and stopAnimation are under the same time, then no animation will show.
But if you invoke startAnimation in -(void)viewWillAppear: and invoke stopAnimation in another message, like followings.
- (void)viewWillAppear:(BOOL)animated
{
[aIndicatorView startAnimation];
// do somethings...
}
- (void)viewDidAppear:(BOOL)animated
{
[aIndicatorView stopAnimation];
}
Because viewWillAppear: and viewDidAppear: are invoked with different event time, the activity indicator view will work well.
Or, you can do something like followings:
- (void)viewWillAppear:(BOOL)animated
{
[aIndicatorView startAnimation];
// Let run loop has chances to animations, others events in run loop queue, and ... etc.
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate date]];
// do somethings ....
[aIndicatorView stopAnimation];
}
The above example is a bad example, because it invokes two or more animations in the -runUntilDate:. But it will let the activity indicator view work.
Create a webview. Add a activity indicator to the webview. If you are loading a image via url into the webview then implement the webview delegate methods. Once the url is loaded then stopanimating the activity indicator.
Let me know which step you are not able to implement.

Don't allow user interaction when activity indicator view is visible

I have a view which contains two views. One of those views contains two buttons and some text labels. The other one, with alpha set to 0.25, has an UIActivityIndicatorView to tell the user that the app is working and he must wait until it finishes. If the user touch a button while the UIActivityIndicatorView is spinning, when the UIActivityIndicatorView stops, the app remember the user action and responds to it. How can I discard the user interaction that occur while the UIActivityIndicatorView is spinning?
Thanks for reading.
P.D.: Like is commented in this thread, I prefer do not to use any modal solution.
EDITED:
I am currently using this code and it does not work right.
- (void)viewDidAppear:(BOOL)animated {
// The view appears with an UIActivityIndicatorView spinning.
[self showResults]; // The method that takes a long time to finish.
[self.activityIndicator stopAnimating];
// When the showResults method ends, the view shows the buttons to the user.
[self.activityIndicatorView setHidden:YES];
[self.menuButton setEnabled:YES];
[self.menuButton setUserInteractionEnabled:YES];
[self.playButton setEnabled:YES];
[self.playButton setUserInteractionEnabled:YES];
[self.view setUserInteractionEnabled:YES];
[self.interactionView setUserInteractionEnabled:YES];
}
I found these methods very useful:
[[UIApplication sharedApplication] beginIgnoringInteractionEvents];
[[UIApplication sharedApplication] endIgnoringInteractionEvents];
In Swift 3.0
To Disable interaction :-
UIApplication.shared.beginIgnoringInteractionEvents()
To restore interaction :-
UIApplication.shared.endIgnoringInteractionEvents()
[_button setUserInteractionEnabled:NO];
That should disable it, just set YES for when you want to user to tap it.
BOOL i_am_ready_to_submit = NO;
-(void)action_finished{
[self.activityIndicator stopAnimating];
i_am_ready_to_submit = YES;
}
-(IBAction)submit_button{
if(i_am_ready_to_submit){
[self submit];
}
}
just add
[self.view setUserInteractionEnabled:NO];
before the
[self.activityIndicator startAnimating];
and reenable it after
[self.activityIndicator stopAnimating];
[self.view setUserInteractionEnabled:YES];
To disable touch event in a view,
[[UIApplication sharedApplication] beginIgnoringInteractionEvents];
To enable touch event in a view
[[UIApplication sharedApplication] endIgnoringInteractionEvents];
For swift 5 you can use:
self.view.isUserInteractionEnabled = false
and to enable all again:
self.view.isUserInteractionEnabled = true
If you have some lag problem, like freeze for about a couple of seconds just put this in:
DispatchQueue.main.async{}
You could disable/enable the UIButtons based on the UIActivityIndicatorView being shown or not. Or, if you just want to "discard the user interaction" while the spinner is shown, in the button handler method:
- (void)buttonTapped:(id)sender {
if ([spinner superview] != nil && [spinner isAnimating]) {
return;
}
// ... the rest of your code
}
This example assumes that when you hide the UIActivityIndicatorView you call one of:
[spinner removeFromSuperview];
or
[spinner stopAnimating];
Use SVProgressHUD WrapperClass It have so many options to show ActivityIndicator
For Source Code Click Here !
[SVProgressHUD showWithMaskType:SVProgressHUDMaskTypeBlack];
use above statement to disable background touches
[SVProgressHUD dismiss]
To enable background touches.
#IBAction func yourButtonPressed(sender: UIButton) {
if self.activityIndicator.isAnimating() {
//remember the action user asked of you using the sender
} else {
//do your stuff
return
}
yourButtonPressed(yourButton)
}
or you code use
self.activityIndicator.animationDidStop to determine when to run your stuff
A quick solution: add a transparent or pseudo transparent view that cover the whole screen. Add your activity indicator on top of this view. When the wait period finishes, remove both views. Get some inspiration.
A better solution, because you can't hide the whole screen in all situations, is to manage the state of the app (ignore actions when the app is 'busy') and disable/enable the appropriate buttons and other controls depending on each app state.
Though answer is replied in earlier response, just like to add for information purpose "[self.activityIndicatorView setHidden:YES];" no need to call this method explicitly, because startAnimating/stopAnimating already take care of this. I'm assuming you are using default value of "hidesWhenStopped" property.