Why doesn't my UIActivityIndicator stop spinning? - objective-c

I have the code below displaying an ActivityIndicator before calling a GCD process. The background process throws a Notification when it's complete or encounters an error. I am calling the stopAnimating method in the error handler but the spinner keeps spinning. Why?
UIActivityIndicatorView *mIndicator;
#interface VC_Main ()
#end
- (void)viewDidLoad
{
[super viewDidLoad];
[NSLog(#"viewDidLoad");
// create indicator for download activity
mIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
[mIndicator setCenter:CGPointMake([self getScreen].x /2.0, [self getScreen].y / 2.0)]; // landscape mode
[self.view addSubview:mIndicator];
// fire off a single interval
[NSTimer scheduledTimerWithTimeInterval:0.0
target:self
selector:#selector(timerTask:)
userInfo:nil
repeats:NO];
...
}
- (void) timerTask:(NSTimer *) timer
{
NSLog(#"DEBUG: timertask timeout");
[mIndicator startAnimating];
...
}
// if there is an error parsing xml downloaded from server, it notifies here
- (void) xmlError:(NSNotification *)note
{
NSLog(#"error parsing xml");
[mIndicator stopAnimating]; // this doesn't work
// fire off a refresh using retry timeout
[NSTimer scheduledTimerWithTimeInterval:TIMEOUT_RETRY_MINS
target:self
selector:#selector(timerTask:)
userInfo:nil
repeats:NO];
NSLog(#"will retry in %d", TIMEOUT_RETRY_MINS);
}

As with every UIKit call, you need to do it on the main thread.
Just do:
dispatch_async(dispatch_get_main_queue(), ^{
[mIndicator stopAnimating];
});
and it should work

Perhaps you retry too soon?
scheduledTimerWithTimeInterval:TIMEOUT_RETRY_MINS
It's supposed to be seconds, not minutes.

Related

Add continuation block to method

I have a singleton that displays status messages:
+ (FFStatusDisplay*) sharedInstance
{
static FFStatusDisplay* sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken,
^{
sharedInstance = [[FFStatusDisplay alloc] init];
});
return sharedInstance;
}
- (void) showStatus:(NSString*)message Timeout:(float)timeout Controller:(UIViewController*)controller
{
if (![self isDisplaying])
{
[self setIsDisplaying:YES];
[self setStatusAlert:[UIAlertController alertControllerWithTitle:nil message:message preferredStyle:UIAlertControllerStyleAlert]];
[controller presentViewController:[self statusAlert] animated:YES completion:
^{
[NSTimer scheduledTimerWithTimeInterval:timeout target:self selector:#selector(timerExpired:) userInfo:nil repeats:NO];
}];
}
}
- (void)timerExpired:(NSTimer *)timer
{
[[self statusAlert] dismissViewControllerAnimated:YES completion:nil];
[self setStatusAlert:nil];
[self setIsDisplaying:NO];
}
I would like to modify showStatus so that I can optionally call it with a completion block that executes after the message is removed by the timer. I think the declaration should be something like this:
- (void) showStatus:(NSString*)message Timeout:(float)timeout Controller:(UIViewController*)controller withBlock:(nullable id (^)(void))block;
But I don't know how to call the block in the body of the implementation. What I've been trying isn't right:
{
if (![self isDisplaying])
{
[self setIsDisplaying:YES];
[self setStatusAlert:[UIAlertController alertControllerWithTitle:nil message:message preferredStyle:UIAlertControllerStyleAlert]];
[controller presentViewController:[self statusAlert] animated:YES completion:
^{
[NSTimer scheduledTimerWithTimeInterval:timeout target:self selector:#selector(timerExpired:) userInfo:nil repeats:NO];
}];
}
block; //this isn't right
}
Can someone show me how to do this?
Thanks
In Objective-C, you call a block like a C function:
block();
That being said, if you want it to fire after your timer has fired, you'll need to add some mechanism to hang onto the block and call it from your timerExpired: method

Why NSTimer doesn't stop with invalidate?

I make a Cocoa application.I create a NSTimer,it can work,but can't stop with invalidate.The following is my code.Anyone else can help me?
#implementation Document
{
NSTimeInterval elapsedTime;
NSTimer *timer;
}
- (IBAction)start:(id)sender
{
elapsedTime = 0.002f;
dispatch_async(dispatch_get_main_queue(), ^{
if(timer == nil)
{
NSLog(#"Starting");
captureFrame = [[CaptureFrame alloc] init];//captureFrame is another object
timer = [NSTimer scheduledTimerWithTimeInterval:elapsedTime target:captureFrame selector:#selector(sendSingleImageImage) userInfo:nil repeats:YES];
NSThread *thread = [NSThread currentThread];
NSLog(#"Current thread is%#",thread);// I want to see the thread.
}
else
{
NSLog(#"Exist!");
[timer invalidate];
timer = nil;
}
});
[_stopButton setEnabled:YES];
[_startButton setEnabled:NO];
}
- (IBAction)stop:(id)sender
{
dispatch_async(dispatch_get_main_queue(), ^{
[timer invalidate];
timer = nil;
NSThread *sthread = [NSThread currentThread];
NSLog(#"S thread is %#",sthread);// I want to see the thread
});
[_startcastButton setEnabled:YES];
[_stopButton setEnabled:NO];
NSLog(#"Stopping");
}
I tried use the following method,but didn't work.
dispatch_async(dispatch_get_main_queue(), ^(void)block)
I also tried to find whether two actions in the same thread,and the result is yes.
So please tell me how to stop the NSTimer.
Any help is appreciated,thanks a lot in advance.

Stop immediately NSTimer

I'm trying to stop my NSTimer (no repeats) immediately but i can't.
my NSTimer do the following code:
-(void)timerAction:(NSTimer*)timer{
dispatch_queue_t serverDelaySimulationThread = dispatch_queue_create("com.xxx.serverDelay", nil);
dispatch_async(serverDelaySimulationThread, ^{
[NSThread sleepForTimeInterval:2.0];
dispatch_async(dispatch_get_main_queue(), ^{
//CALL WEBSERVICE RESPONSE IN HERE
});
});
}
Each time textField Did Change, i will invalidate NSTimer and recreate NSTimer!! All i want to execute a request to WEBSERVICES if the Delay time between 2 times user typing characters bigger than 2s. My code is following
-(void) textFieldDidChange:(id)sender{
[MyTimer invalidate];
MyTimer = nil;
MyTimer = [NSTimer scheduledTimerWithTimeInterval:0 target:self selector:#selector(timerAction:) userInfo:nil repeats:NO];
}
But, when i quickly type "ABCD" -> 2s after that, 4 webservices was called.
How can i stop NSTimer while it unfinished
Tks for advance!
-(BOOL) textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
[myTimer invalidate];
myTimer = nil;
myTimer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:#selector(timerFired) userInfo:nil repeats:NO];
return YES;
}
This worked for me.
Your timer timeInterval is set to 0. So each time you type your timer fires. Change your code to look like this
MyTimer = [NSTimer scheduledTimerWithTimeInterval:2.0
target:self selector:#selector(timerAction:) userInfo:nil repeats:NO];
And it should work :)
The problem is you are implementing textdidchange delegate. So how many times you type text. It will get called. SO inside that you have written four letters. So definitely your NSTimer is calling four times your web service. I would suggest better to use - (void)controlTextDidEndEditing:(NSNotification *)aNotification So that whenever you enter or tabb out your webservice will called.

Not understanding how UIActivityIndicatorView works with NSTimer

I'm missing something about how UIActivityIndicatorView and NSTimer work together.
I've added this UIActivityIndicatorView in Interface Builder with the following settings:
The UIWebView is instantiated as self.webV and the UIActivityIndicatorView as self.indicator.
I have the following code in the implementation file:
-(void)viewDidLoad
{
[super viewDidLoad];
//Create UIWebView.
if (!self.webV)
{
self.webV = [[UIWebView alloc] init];
}
self.webV.delegate = self;
//Load web page.
NSString *baseURLString = #"myURL.com";
NSString *urlString = [baseURLString stringByAppendingPathComponent:#"myURL.com"];
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0/2.0 target:self selector:#selector(timerLoad) userInfo:nil repeats:YES];
[self connectWithURL:urlString andBaseURLString:baseURLString];
}
-(void)timerLoad
{
if (!self.webV.loading)
{
[self.indicator stopAnimating];
}
else
{
[self.indicator startAnimating];
}
}
But when the UIWebView loads, no activity indicator shows up. What am I doing wrong or leaving out?
Thanks for the help, folks.
I'm really not sure on what the behaviour of the UIActivityIndicatorView is supposed to be if you repeatedly call start/stop on it. I am reasonably sure it isn't meant to be used that way :)
So, even though your question is specific to NSTimer and UIActivityIndicatorView, it may be helpful to understand that you should approach your solution differently.
Instead of using a timer that repeatedly calls [self.indicator startAnimating] every half-second, you should use the webview delegate methods to toggle the UIActivityIndicatorView on and off.
-(void)viewDidLoad
{
[super viewDidLoad];
//Create UIWebView.
if (!self.webV)
{
self.webV = [[UIWebView alloc] init];
}
self.webV.delegate = self;
//Load web page.
NSString *baseURLString = #"myURL.com";
NSString *urlString = [baseURLString stringByAppendingPathComponent:#"myURL.com"];
//self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0/2.0 target:self selector:#selector(timerLoad) userInfo:nil repeats:YES];
[self connectWithURL:urlString andBaseURLString:baseURLString];
}
- (void)webViewDidStartLoad:(UIWebView *)webView{
//start animating
[self.indicator startAnimating];
}
- (void)webViewDidFinishLoad:(UIWebView *)webView{
//stop animating
[self.indicator stopAnimating];
}
There could be several reasons. It could load so fast that the loading is already done, or it never loaded at all because something is wrong with the URL.
This isn't the cause of your problem, but you never invalidate your timer, which you should.
I was also going to make the point that you should use delegate methods instead of a timer, but pdriegen beat me to it.

NSTimer is Not stopping when double Click on a Button

I have NSTimer to display an image after 2 seconds when i click on button. It is working fine when I Click on a Button, But the problem is When I double Click the Button the NSTimer is Not Stopping. Continuously it is displaying images(calling NSTimer method) without Clicking on the button for Next Time. How to stop the NSTimer when i double Click / more Clicks on that button at a time.
*This is my Code*
-(void)buttonClicked
{
timer = [NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:#selector(timerClicked) userInfo:nil repeats:YES];
imgviewtransparent.image = [UIImage imageNamed:[imagesArray objectAtIndex:i]];
}
-(void)timerClicked
{
[timer invalidate];
timer = nil;
}
With double Click it will schedule timer twice.
You can create a BOOL property to avoid this if you want.
-(void)viewDidLoad{
[super viewDidLoad];
//Define all your implementation
//Set BOOL property to no at start
self.isButtonClicked = NO; // You need to define this BOOL property as well.
}
-(void)buttonClicked {
// Return if one click is already in process
if (self.isButtonClicked) {
return;
}
self.isButtonClicked = YES:
timer = [NSTimer scheduledTimerWithTimeInterval:0.2 target:self
selector:#selector(timerClicked) userInfo:nil repeats:YES];
imgviewtransparent.image = [UIImage imageNamed:[imagesArray objectAtIndex:i]];
}
-(void)timerClicked {
[timer invalidate];
timer = nil;
self.isButtonClicked = NO;
}