Application crashing due to Nested NSTimer and view switch - objective-c

here is the code
-(void)manageAnimation:(Properties *)prop
{
//if(bounceTimer.isValid)
// [bounceTimer invalidate];
bounceTimer = [NSTimer scheduledTimerWithTimeInterval:.05 target:self selector:#selector(animate) userInfo:nil repeats:YES];
}
-(void)animate{
static float t = 0;
float d = .5;
static float fValue = 0;
fValue = [self easeOutBounce:t andB:0 andC:34 andD:d];
[self setNeedsDisplay];
t += .5/10;
if(t > d){
[bounceTimer invalidate];
t = 0;
fValue = 0;
}
}
I am calling manageAnimation after each second. When i click info button on screen it flips to settings screen, when i click done button on settings screen to go back to main screen the application crashes. If i comment out the code
//bounceTimer = [NSTimer scheduledTimerWithTimeInterval:.05 target:self selector:#selector(animate) userInfo:nil repeats:YES];
Then application works ok. I am not sure whats going on, also i found that when i switched to settings view these timers are stopped.

Why would you call manageAnimation every second? If you do that then you create a new timer every time and still have the previous timer running.
Since the timer repeats you can just let it run instead of creating a new one every time you call manageAnimation.
Also you need to retain the timer you create. I'm assuming that bounceTimer is an ivar, so you need this in your .h file:
#property (nonatomic, retain) NSTimer *bounceTimer;
And then manageAnimation needs to use the setter:
-(void)manageAnimation:(Properties *)prop
{
if (self.bounceTimer == nil) {
self.bounceTimer = [NSTimer scheduledTimerWithTimeInterval:.05 target:self selector:#selector(animate) userInfo:nil repeats:YES];
}
}
And in animate where you invalidate the timer, set the ivar to nil to release it:
[bounceTimer invalidate];
self.bounceTimer = nil;

Related

How do I set a NSTimer to stop if a certain action is running?

I have a popup in a custom view controller that is presented after 1 minute thanks to my NSTimer.
My NSTimer code:
[NSTimer scheduledTimerWithTimeInterval:60.0f
target:self selector:#selector(methodB:) userInfo:nil repeats:YES];`
The method that reveals the popup
- (void) methodB:(NSTimer *)timer
{
//Do calculations.
[self showPopupWithStyle:CNPPopupStyleFullscreen];
}
I'm trying to set the NSTimer to stop running if the [self showPopupWithStyle:CNPPopupStyleFullscreen]; is currently open, running or active.
Then I would like to start the NSTimer back up again if the popup view controller is NOT open, running or active.
Any help, samples or examples would be greatly appreciated!
My project is written in Objective-C.
EDIT:
I tried the answers in the suggested "possibly similar" answer and it doesn't seem to work for what I am doing. How do I stop NSTimer?
This seems to do the trick.
#property (nonatomic, strong) CNPPopupController *popupController;
- (void)_timerFired:(NSTimer *)timer;
#end
NSTimer *_timer;
- (void)viewDidLoad {
[super viewDidLoad];
if (!_timer) {
_timer = [NSTimer scheduledTimerWithTimeInterval:10.0f
target:self
selector:#selector(_timerFired:)
userInfo:nil
repeats:YES];
}
}
- (void)_timerFired:(NSTimer *)timer {
if ([_timer isValid]) {
[self showPopupWithStyle:CNPPopupStyleFullscreen];
[_timer invalidate];
}
_timer = nil;
NSLog(#"ping");
}
If you want to restart the timer just add this code where you'd like the action to start back up.
if (!_timer) {
_timer = [NSTimer scheduledTimerWithTimeInterval:10.0f
target:self
selector:#selector(_timerFired:)
userInfo:nil
repeats:YES];
}
Hope this helps! :)

Xcode cocoa simultaneously update interface by timer and continuous action by slider

I need to update some user interface objects by timer, but when I touch slider with continuous action everything freeze beside slider.
in iOS this version work fine, but in mac os x some problems :(
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
tick = 0;
[NSTimer scheduledTimerWithTimeInterval:0.25f
target:self
selector:#selector(timerTick)
userInfo:nil
repeats:YES];
}
- (void)timerTick
{
tick++;
[self.labelTest setIntegerValue:tick];
}
- (IBAction)sliderAction:(id)sender
{
// do something
NSLog(#"%g", [self.sliderMain doubleValue]);
}
You should add your timer to main run loop:
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
And also you should create instance variable or property, for example:
#property (strong, nonatomic) NSTimer *timer;
And before you create timer I would recomentded you to use lazy initialization:
if (!_timer) {
_timer = [NSTimer scheduledTimerWithTimeInterval:0.25f
target:self
selector:#selector(timerTick)
userInfo:nil
repeats:YES];
}
[[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
And when you close the app or the app goes to background you can invalidate timer:
[self.timer invalidate];
self.timer = nil;
Hope this help.

NSTimer Not Stopping When Invalidated

I have the following code in my .h file:
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#import <AVFoundation/AVFoundation.h>
#import <MapKit/MapKit.h>
#interface LandingController : UIViewController<CLLocationManagerDelegate> {
CLLocationManager *LocationManager;
AVAudioPlayer *audioPlayer;
}
#property (nonatomic, retain) NSTimer *messageTimer;
- (IBAction)LoginButton:(id)sender;
#end
I have the following code in my .m file:
#interface LandingController ()
#end
#implementation LandingController
#synthesize messageTimer;
- (void)checkForMessages
{
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:#"BINGO:"
message:#"Bingo This Works"
delegate:nil
cancelButtonTitle:#"Okay"
otherButtonTitles:nil];
[alert show];
}
- (IBAction)LoginButton:(id)sender {
if ([UserType isEqualToString:#"Owner"]) {
if (messageTimer){
[self.messageTimer invalidate];
self.messageTimer = nil;
}
} else {
if (!messageTimer){
self.messageTimer = [NSTimer scheduledTimerWithTimeInterval:10.0
target:self
selector:#selector(checkForMessages)
userInfo:nil
repeats:YES];
}
}
}
#end
But my timer doesn't want to stop when I call the invalidate.
The LoginButton is only pressed twice, once when the strResult is = to "Guard" and then the application changes it to be equal to "Owner" and the user presses the login button again, so I don't think I'm setting multiple timers.
After pressing the login button and starting the timer I segue to another view and then segue back to press the login button once more which is when I want the timer to stop. Do I need to do anything special to get the messageTimer since I switched views for a moment and then came back?
Any ideas?
Thanks!
You need to call [self.messageTimer invalidate] on the same thread on which you created the timer. Just make sure that the timer is created and invalidated on main thread.
dispatch_async(dispatch_get_main_queue(), ^{
if ([UserType isEqualToString:#"Owner"]) {
[self.messageTimer invalidate];
self.messageTimer = nil;
} else {
self.messageTimer = [NSTimer scheduledTimerWithTimeInterval:10.0
target:self
selector:#selector(checkForMessages)
userInfo:nil
repeats:YES];
}
});
NSTimer is retained by NSRunLoop, so the only way I see your issue happening is if you're actually creating more than one timer and invalidating only what you have reference to.
Example:
if(!timer)
timer = [NSTimer scheduledTimerWithTimeInterval:1 target:(self) selector:#selector(processTimer) userInfo:nil repeats:YES];
Have you try to put repeat as No
self.messageTimer = [NSTimer scheduledTimerWithTimeInterval:10.0
target:self
selector:#selector(checkForMessages)
userInfo:nil
repeats:NO];
If the code at the end (starting with if) is called twice with UserType != Owner, you create a new timer without invalidating the old one. Now two timers are running. If the code executes a third time, you will add a third timer and so on. Executing the code with UserType == Owner only invalidates the last timer and even it is called repeatly, it does not invalidate older timers.
You have a timer leak. ;-)
How about put an NSLog in your checkForMessages method? It would be easier to check if there's really only 1 timer.
(I'd rather put this in a comment, but I don't have that much reputation....)
I have an approach for stopping or deactivate the timer, Before apply this make sure that you tried all the cases as mentioned above so you can also understand that why this approach used at last.
// Instead of self use NSTimer class, it will not provide you any crash and in selector placed any empty function and setRepeats to NO
self.messageTimer = [NSTimer scheduledTimerWithTimeInterval:100.0
target:NSTimer selector:#selector(emptyFunctionCalling)
userInfo:nil
repeats:NO];
[self.messageTimer invalidate];
self.messageTimer = nil;
So whenever the case occured that timer will not stopping then entering in this code the timer will stops permanently.
Its weird but invalidating passed timer reference and created timer reference worked for me.
delayTimer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(updateDelayLable:) userInfo:nil repeats:YES];
-(void)updateSendDataDelayLable:(NSTimer*)timer{
delayValueForGNSS--;
if (delayValueForGNSSSend==0) {
[timer invalidate];
timer = nil;
[delayTimer invalidate];
delayTimer = nil;
}
}

Pause and resume Timer in ViewController

I want to pause and resume timer in viewcontroller which is displayed as subview in mainviewcontroller. Play/Pause button in mainviewcontroller when pressed it displays view controller as subview as well as plays audio.
Have this NSTimer in view controller
[NSTimer scheduledTimerWithTimeInterval:2.6
target:self
selector:#selector(updateView:)
userInfo:nil
repeats:YES];
- (void)updateView:(NSTimer *)theTimer
{
if (index < [textArray count])
{
self.textView.text = [self.textArray objectAtIndex:index];
self.imageView.image = [self.imagesArray objectAtIndex:index];
index++;
} else {
index = 0;
}
I want to pause and resume timer when play/pause button is pressed in mainviewcontroller. How can i do that.
NSTimer* myTimer = [NSTimer scheduledTimerWithTimeInterval:2.6
target:self
selector:#selector(updateView:)
userInfo:nil
repeats:YES];
[myTimer invalidate];
/*
Stops the receiver from ever firing again and requests its removal from its run loop.
*/
[myTimer fire];
/*
You can use this method to fire a repeating timer without interrupting its regular firing schedule. If the timer is non-repeating, it is automatically invalidated after firing, even if its scheduled fire date has not arrived.
*/
First you have to keep a reference of your timer in your interface-file:
NSTimer *myTimer;
In the implementation file you can simply connect and then refer to it:
myTimer = [NSTimer scheduledTimerWithTimeInterval:2.6
target:self
selector:#selector(updateView:)
userInfo:nil
repeats:YES];
To stop the timer call:
[myTimer invalidate];
To start it again:
[myTimer fire];
See NSTimer Class Reference for more info about the NSTimer Class.

UIButton Touch and Hold

I haven't found a very easy way to do this. The ways I've seen require all these timers and stuff. Is there any easy way I can hold a UIButton and cause it to repeat the action over and over until it gets released?
You can do the following: Make an NSTimer that will start up when the app starts or in viewDidLoad and also make a boolean.
For example:
//Declare the timer, boolean and the needed IBActions in interface.
#interface className {
NSTimer * timer;
bool g;
}
-(IBAction)theTouchDown(id)sender;
-(IBAction)theTouchUpInside(id)sender;
-(IBAction)theTouchUpOutside(id)sender;
//Give the timer properties.
#property (nonatomic, retain) NSTimer * timer;
Now in your implementation file (.m):
//Synthesize the timer
#synthesize timer;
//When your view loads initialize the timer and boolean.
-(void)viewDidLoad {
g = false;
timer = [NSTimer scheduledTimerWithInterval: 1.0 target:self selector:#selector(targetMethod:) userInfo:nil repeats: YES];
}
Now make an IBAction for "Touch Down" set the boolean to lets say true. Then make another IBAction button for "Touch Up Inside" and "Touch Up Outside" assign the boolean to false.
For example:
-(IBAction)theTouchDown {
g = true;
}
-(IBAction)theTouchUpInside {
g = false;
}
-(IBAction)theTouchUpOutside {
g = false;
}
Then in that NSTimer method, put the following:(assume g is the boolean you have declared)
-(void) targetmethod:(id)sender {
if (g == true) {
//This is for "Touch and Hold"
}
else {
//This is for the person is off the button.
}
}
I hope this simplifies everything... I know it still uses a timer but there is not another way.
Unfortunately, it still looks like you have to code this functionality for yourself. simplest way (You still need a timer though):
A function that performs the action you want to repeat:
-(void) actionToRepeat:(NSTimer *)timer
{
NSLog(#"Action triggered");
}
in your .h file declare and set a property for a timer:
#interface ClassFoo
{
NSTimer* holdTimer;
}
Then in the .m make two IBActions:
-(IBAction) startAction: (id)sender
{
holdTimer = [NSTimer scheduledTimerWithTimeInterval:0.4 target:self selector:#selector(actionToRepeat:) userInfo:nil repeats:YES];
[holdTimer retain];
}
-(IBAction) stopAction: (id)sender
{
[holdTimer invalidate];
[holdTimer release];
holdTimer = nil;
}
Then Just link to the Touch Down event in IB from the button to startAction and the Touch Up Inside to the 'Stop Action'. It isn't a one liner but it allows you to customise the rate the action repeats as well as allowing you to trigger it from another outlet/action.
You might consider subclassing UIButton and adding this functionality if you are going to be using this functionality often - then it is only (slightly) painful to implement the first time.
An other way to use this NBTouchAndHoldButton. This is exactly what you want, and very easy to implement it:
TouchAndHoldButton * pageDownButton = [TouchAndHoldButton buttonWithType:UIButtonTypeCustom];
[pageDownButton addTarget:self action:#selector(pageDownAction:) forTouchAndHoldControlEventWithTimeInterval:0.2];
Good luck!
I cannot reply to the first one, but this line:
timer = [NSTimer scheduledTimerWithInterval: 1.0 target:self selector:#selector(targetMethod:) userInfo:nil repeats: YES];
for at least iOS 4.1 and newer needs to be:
timer = [NSTimer scheduledTimerWithTimeInterval: 1.0 target:self selector:#selector(targetMethod:) userInfo:nil repeats: YES];
I know this is an old question, but as an easy way, like to consider using "[NSObject performSelector:withObject:afterDelay:]" to repeatedly invoke methods in any particular time interval.
In this case:
NSTimeInterval someTimeInterval = 1;
- (IBAction)action:(id)sender {
UIButton * const button = sender;
if (button.state != UIControlStateHighlighted) {
return;
}
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:_cmd object:sender];
[self performSelector:_cmd withObject:sender afterDelay:someTimeInterval];
}