startAnimating is not working inside the IBAction of a button press - objective-c

I am having a strange problem.I am having a button , and on pressing the button I am doing
the following
- (IBAction)btnPressed:(id)sender {
[ _spinner startAnimating];//where _spinner is a ActivityIndicator
[self showResult];
}
-(void) showResult{
sleep(10);
resultText.text=#"Hi..Sorry.It has been a long wait";
}
But the spinner never starts animating , even though the showResult method gets called
properly. I feel this very strange . When I put [_spinner startAnimating] in the viewDidLoad
it works perfect. Any help is greatly appreciated.

sleep method get called on the main thread causing UI to hang, that's why you aren't able to see the activity indicator
one way to do this:
- (IBAction)actionbuton:(id)sender {
[_activity startAnimating];
[self performSelectorInBackground:#selector(backgroundProcess) withObject:nil];
}
- (void) backgroundProcess{
sleep(4);
[self performSelectorOnMainThread:#selector(processingDone) withObject:nil waitUntilDone:NO];
}
- (void) processingDone{
[_activity stopAnimating];
}

First start your acivity inside button press method.
Then after call your showResult method in timer.
I think that will resolve your problem.
Also remove Sleep from showResult.

Related

shouldPerformSegueWithIdentifier always executed first

I have a function called "openButtonPressed" which gets executed when a button on the ui gets pressed. Now I would like to show a loading view at first and then execute the segue. For some reason, the segue always gets called first.
Does somebody have a clue?
Thank you!
- (void)openButtonPressed:(id)sender
{
NSDebug(#"Open Button");
[self showLoadingView];
static NSString* segueToContinue = kSegueToContinue;
if ([self shouldPerformSegueWithIdentifier:segueToContinue sender:self]) {
[self performSegueWithIdentifier:segueToContinue sender:self];
}
}
- (void)showLoadingView
{
if(!self.loadingView) {
self.loadingView = [[LoadingView alloc] init];
[self.view addSubview:self.loadingView];
}
[self.loadingView show];
}
It looks like LoadingView is being initialized without a size.
It also may be executing the segue very quickly and you don't have any time to see the loading view.
You can set a breakpoint at the performSegue call and use the view debugger in Xcode to confirm if the view was loaded and what size the view was initialized to.
Maybe the view is not in the right place or in the right size. But even if you fix that you need to add a delay so the user can see the loading... I replicated your scenario here and after giving the view a initWithFrame it appears, but cannot be seen because is too fast...
If you want to show the loading for a period of time before the performSegue, you could use the performSelector:withObject:afterDelay.
If you need to wait an action from the loadingView, create a completionHandler in the loadingView initializer...
The hints below were very helpful to me and totally a better solution but I ended up using [CATransaction flush]; to force the refresh.
- (void)openButtonPressed:(id)sender
{
NSDebug(#"Open Button");
[self showLoadingView];
[CATransaction flush];
static NSString* segueToContinue = kSegueToContinue;
if ([self shouldPerformSegueWithIdentifier:segueToContinue sender:self]) {
[self performSegueWithIdentifier:segueToContinue sender:self];
}
}

Closing OSX window on load

I have come up with a taskbar program that is able to shut and close a window. Key code being:
-(void) openWindow{
NSLog(#"Opening Window");
//put infront of all other apps
[NSApp activateIgnoringOtherApps:YES];
//show window
[[_myView window] orderFront:self];
}
-(void) closeWindow{
NSLog(#"Closing Window");
//hide window
[[_myView window] orderOut:self];
}
This is working perfectly.
The only issue is, I now want the program to start off with the window closed but when I set this:
- (void)viewDidLoad {
[super viewDidLoad];
[self closeWindow];
}
Nothing happens and the window stays open?? And before you ask - Yes 'Visible At Launch' is switched off! Haha
If I add a 0.001 second delay in the view did load it works!!
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.001 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[self closeWindow];
});
but then there is a horrible flicker...
If there is a viewWillAppear method, try to put your [self closeWindow] in.
Or maybe you try to close the window before she is visible so the code to show window is executed after your call to closeWindow.
Put some breakpoints if needed to see the order of the methods's execution.

Transition between views as if one was modal

I've got a login screen I want to hide like:
[self dismissModalViewControllerAnimated:true];
But the problem is that I need to display it like this:
-(void)viewDidAppear:(BOOL)animated {
[self presentModalViewController:loginScreen animated:false];
}
Which means I'll flash the current screen before I popup the login screen.
So what I'm looking for is a way to show the login screen instantly and transition to the main screen with the same animation as dissmissModalViewControllerAnimated:true.
You should be able to just disable the animation so its instant?
[self presentModalViewController:loginScreen animated:NO];
Do it in viewWillAppear if it still flashes briefly.
What I've done (maybe not the best solution):
- (void)viewDidLoad
{
// Initial set to hidden for avoiding a flickering UI
self.view.hidden=YES;
}
-(void)viewWillAppear:(BOOL)animated
{
[NSTimer scheduledTimerWithTimeInterval:0 block:^{
[self presentModalViewController:self.loginViewController animated:NO];
} repeats:false];
}
And just before you dispatch the ModalViewController you set self.view.hidden=NO.
See https://github.com/jivadevoe/NSTimer-Blocks for the NSTimer using blocks

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.

In Cocoa, why won't a textfield be shown until after the IBAction is completely executed?

I have an IBAction with some simple code inside:
-(IBAction)change:(id)sender {
[textfield setHidden:NO];
[self dolengthyaction];
}
'textfield' is an NSTextField in a nib file, and -'dolengthyaction' is a function that takes about a minute to finish executing.
My question is: Why isn't the textfield shown until AFTER "dolengthyaction" is done executing? I want it to be revealed before the dolengthyaction starts taking place. Is this an inherent problem or is there something wrong with my code? (or in another part of my code?)
I'm still not very good at programming so I apologize if I worded something badly and formatted something wrong.
EDIT: There isn't much else other than this IBAction and -dolengthyaction...
-(void)doLengthyAction {
sleep(10);
}
-(IBAction)change:(id)sender {
[textfield setHidden:NO];
[self doLengthyAction];
[textfield setHidden:YES];
}
All I really want to do is display the label when the action is running and hide it when it's done.
Basically what this means is that it's not displaying at all right now.
In reality, in -doLengthyAction it's not sleep(10) but rather a NSFileManager operation that copies about 50 Mb of material. The code was rather long, but if you want me to post it I can. I tested it with sleep() but it doesn't work either.
All drawing operations (including hiding and showing views) are triggered from the run loop. The run loop cannot perform the next event until after your function returns.
If you have an operation that takes more than a second to run, you should perform it in a thread. Once it completes, use performSelectorOnMainThread to make UI changes on the main thread.
As mentioned in some of the previous answers, the application must return to the main run loop before it will redraw (it's an optimization to avoid redrawing when you make many changes).
You really should handle things on a background thread if they're going to run for a long time. If you don't the UI will beach ball while your operation runs.
If you're targeting 10.6+, you can use GCD and blocks as follows to run things easily in the background (and not have to define multiple methods to get things done):
-(void)doLengthyAction {
sleep(10);
}
-(IBAction)change:(id)sender {
[textfield setHidden:NO];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self doLengthyAction];
dispatch_async(dispatch_get_main_queue(), ^{
[textfield setHidden:YES];
});
});
}
Of course by using this, you'll need to make sure that what's going on in the lengthy action is thread safe, but you'll end up with a better user experience. For more info on this type of CGD code, you might want to read this.
I think there is something wrong with the rest of your code. This should not happen.
Post more?
Can't say what exactly is transpiring with out looking at rest of the code, but a hackish way would be to try:
-(IBAction)change:(id)sender {
[textfield setHidden:NO];
[self performSelector:#selector(doLenghtyAction) withObject:nil afterDelay:0.1]; --> or maybe even 0.0
}
-(void)doLengthyAction {
sleep(10);
[textfield setHidden:YES];
}
-(IBAction)change:(id)sender {
[textfield setHidden:NO];
[NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(doLengthyAction) userInfo:nil repeats:NO];
}
Try this:
-(IBAction)change:(id)sender {
[textfield setHidden:NO];
[[textfield window] display]; // force the window to update
[self doLengthyAction];
[textfield setHidden:YES];
}