I have one view controller (let's call it ViewController) with timer within its code, and have another view controller (let's call it ContentViewController) with webView within it which shows content depending on which button is clicked.
I have buttons on ViewController which, when clicked, pushes ContentViewController which loads a html file into its webView.
Timers are used to close ContentViewController if it is pushed.
Here's how I create timers:
-(void)createTimer :(NSNumber*)index
{
if (_show)
{
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:index, #"parameter1", nil];
switch ([index intValue])
{
case 1001:
[NSTimer scheduledTimerWithTimeInterval:10.0 target:self selector:#selector(updateUi:) userInfo:dictionary repeats:YES];
break;
case 1002:
[NSTimer scheduledTimerWithTimeInterval:10.0 target:self selector:#selector(updateUi:) userInfo:dictionary repeats:YES];
break;
case 1003:
[NSTimer scheduledTimerWithTimeInterval:20.0 target:self selector:#selector(updateUi:) userInfo:dictionary repeats:YES];
break;
case 1004:
[NSTimer scheduledTimerWithTimeInterval:20.0 target:self selector:#selector(updateUi:) userInfo:dictionary repeats:YES];
break;
}
}
}
Here's the code in ViewController which closes ContentViewController when timer fired:
-(void)updateUi :(NSTimer *)timer
{
int index = [[timer.userInfo objectForKey:#"parameter1"] intValue];
if([self.navigationController.visibleViewController isKindOfClass:[ContentViewController class]])
{
if ([[[NSUserDefaults standardUserDefaults]valueForKey:#"CurrentString"] intValue]==index )
{
[self.navigationController popViewControllerAnimated:YES];
}
}
}
And on web page which is shown on ContentViewController there's a button,when user clicks it ContentView must go back to ViewController. Here's how I do this:
- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType
{
NSURL *URL = [request URL];
if ([[URL scheme] isEqualToString:#"coffee-drink"])
{
NSString *urlString = [[request URL] absoluteString];
NSArray *urlParts = [urlString componentsSeparatedByString:#":"];
if (urlParts.count > 1) ///------------поменял
{
[self.navigationController popViewControllerAnimated:YES];
}
}
return YES;
}
All of above works fine but sometimes it crashes with the following error:
[ContentViewController respondsToSelector:]: message sent to deallocated instance
And also, I have other errors like these:
nested push animation can result in corrupted navigation bar
Finishing up a navigation transition in an unexpected state. Navigation Bar subview tree might get corrupted.
and
delegate (webView:decidePolicyForNavigationAction:request:frame:decisionListener:) failed to return after waiting 10 seconds
The last one appears very rarely.
I don't know what am I doing wrong.Can someone help me)?If needed any more information I will provide it!Thanks in advance!
Issue
You are popping your view without leaving ARC to deallocate all objects and delegates, by keeping them alive.
My Solution
For the NSTimer
At #implementation create NSTimer *timer and use it when you want to initialize it. To dealloc it correctly when you pop back, at viewWillDisappear set [timer invalidate] and timer = nil.
For delegates
Set you specific delegates to nil. For example self.delegate = nil
#Yuandra Ismiraldi is right. Your NSTimer will call your updateUI method every 20/10 seconds repeatedly untill it receives [timer invalidate]; instruction. You are getting this error messages as you navigate through your view controller tree. Sometimes your NavigationViewController will need more memory and will release some of un-used objects. In your case some of the previously shown view controllers that still runs the updateUI method every 20/10 seconds. Once this view controller has been released and your selector is called you will receive this crash.
I hope this explains why sometimes you get this crash, and sometimes not.
Your timer code
[NSTimer scheduledTimerWithTimeInterval:10.0 target:self selector:#selector(updateUi:) userInfo:dictionary repeats:YES]
the repeat parameter is set to YES, this make your timer always repeat, thus repeating the sending of the message updateUI after the timer has run for the first time and the view has popped, thus resulting in your error. To make sure your timer only run once, set the repeat to NO
[NSTimer scheduledTimerWithTimeInterval:10.0 target:self selector:#selector(updateUi:) userInfo:dictionary repeats:NO]
Related
I have used NSTimer & at the end of countDown want to change the value of View.UILabel.text=#"Time Up". This is working perfectly until I switch to some other ViewController, after switching between viewController(& coming back to this viewController) when countDown is complete I am getting perfect values & their is no nil but View.UILabel.text value is not changing, I have checked exceptions, no exception get raised.
My Code:
-(void) timeInterval
{
appdelegate._UserProfile.totalTime = 30;
UIApplication *app = [UIApplication sharedApplication];
//will allow NSTimer to continue running in background..
UIBackgroundTaskIdentifier bgTask = 0;
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
}];
appdelegate._UserProfile.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(countDown) userInfo:nil repeats:YES];
}
-(void) countDown
{
appdelegate._UserProfile.totalTime -= 1;
if(appdelegate._UserProfile.totalTime == 0)
{
[profileView.userPhoneNo setText:#""];
profileView.timeUp.text= #"* Time Up";
}
}
Please any help will be very appreciated. (I have seen many questions but my issue is not solved)
*Note: After switch if i change the value of label in ViewWillAppear() it is working perfect, but i need to change text when countDown is completed. I am using UINavigationController & MFSideMenuManager to switch between ViewControllers.
My Navigation Code:
-(void) initMyProfile
{
UINavigationController *detailNavigationController = [MFSideMenuManager sharedManager].navigationController;
detailNavigationController.menuState = MFSideMenuStateHidden;
MyProfileViewController_iPhone* detailViewController = [[MyProfileViewController_iPhone alloc] initWithNibName:nil bundle:nil];
detailViewController.title = #"My Profile";
detailNavigationController.viewControllers = [NSArray arrayWithObject:detailViewController];
}
One possible explanation is that you are not coming back to the original view controller, but to a fresh new instance. This can easily happen when using a standard segue instead of an unwind segue.
See this other question where a similar problem occurred: Changing label text inside method
If you are returning to the view by clicking on the back navigation button, and calling this methods on viewDidLoad that is the problem.
The viewDidLoad are only called once the view controller is created, to call those methods every time when you view controller opens, call those methods on viewWillAppear.
In My Application i need to release my NSTimer , when i am moving from one view controller to another view controller . How to release this type of objects in ARC ? i am using below code for creation and releasing NSTimer but Where i have to write this releasing code in view controller?
For Creation.
- (void)viewDidLoad{
[super viewDidLoad];
updateBCK = [NSTimer scheduledTimerWithTimeInterval:(5.0) target:self selector:#selector(changeImage) userInfo:nil repeats:YES];
[updateBCK fire];
}
-(void)changeImage{
static int i=0;
if (i == [myImages count]){
i=0;
}
[UIImageView beginAnimations:nil context:NULL];
[UIImageView setAnimationDuration:0.6];
mainBackgroundImageView.alpha=1;
mainBackgroundImageView.image =[myImages objectAtIndex:i];
NSLog(#"\n The main screen image is %#",[myImages objectAtIndex:i]);
[UIImageView commitAnimations];
i++;
}
For Release.
[updateBCK invalidate];//
updateBCK = nil;
Thanks in Advance.
You should call
[timer invalidate];
timer = nil;
where you push your view controller. If this is an issue, you can still call it in
- (void) viewWillDisappear:(BOOL)animated;
Also, you should initialize it in
- (void) viewDidAppear:(BOOL)animated;
That makes more sense.
When you want to stop time
use below code
[timer invalidate]; // timer is the name of timer object
- (IBAction)button:(id)sender {
SecondViewController *second = [[SecondViewController alloc]
initWithNibName:#"secondViewController"
bundle:nil];
[self presentViewController:second animated:NO completion:nil];
[self.timer invalidate]; // timer is the name of timer object
timer=nil;//it may work without this line too ;not sure
}
https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Timers/Articles/usingTimers.html
A timer maintains a strong reference to its target. This means that as long as a timer remains valid, its target will not be deallocated. As a corollary, this means that it does not make sense for a timer’s target to try to invalidate the timer in its dealloc method—the dealloc method will not be invoked as long as the timer is valid.
when i am moving from one view controller to another view controller
Then you should do this in delloc, if you want to do some cleanup tasks when your view is dismissing or released. It's the best place, In such case you can implement it.
-(void)dealloc{
[updateBCK invalidate];//
updateBCK = nil;
}
Hope this helps
I am showing a count down timer using UILabel and NSTimer -
-(void)a_Method
{
[coolTimeLbl setNeedsDisplay];
coolTime = 5; // it is an int
coolingTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(cooling) userInfo:nil repeats:YES]; // NSTimer
}
-(void)cooling
{
if (coolTime>0)
{
coolTime = coolTime-1;
NSLog(#" coolTime----%#",coolTime);
coolTimeLbl.text =[NSString stringWithFormat:#"%d",coolTime];
NSLog(#" coolTimeLbl----%#",coolTimeLbl.text);
}
else
{
[coolingTimer invalidate];
coolingTimer = nil;
}
}
The first time everything works fine and I am getting coolTimeLbl.text as - 4 3 2 1 0
But the second time when I call aMethod, coolTimeLbl is not getting updated properly - it is like 3 2 0 etc (some weird behavior)
However both NSLogs (coolTime & coolTimeLbl) print perfectly all the times and values.
Why does this happen? I tried many ways like NSNotification etc.
Please help me to fix this.
If you're calling a_Method more than once before coolingTimer invalidates itself, the timer will tick more than once.
You should add some boolean like ;
BOOL isTimerValid;
in a_Method,
if(!isTimerValid)
{
isTimerValid = YES;
coolingTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(cooling) userInfo:nil repeats:YES]; // NSTimer
}
in cooling,
else
{
isTimerValid = NO;
....
}
Had the same issue in one of my viewControllers and another one was working OK with same NSTimer code. Looked at about 20 SO threads to get it solved. No luck. In my case
myLabel.opaque = false
solved it.
Don't ask me why.
My UIWebView is working perfectly in simulator, but not on all my test devices, the UIWebView remains blank. Any suggestions to edit this code?
- (void)awakeFromNib
{
[worship loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.website.com/current.pdf"] cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:60.0]];
timer = [NSTimer scheduledTimerWithTimeInterval:(1.0/2.0) target:self selector:#selector(tick) userInfo:nil repeats:YES];
}
Try moving your code into:
- (void)viewDidLoad {
//Here
}
Your webview is probably = nil in
-(void)awakeFromNib {
}
Edit: You can also add NSLog(#"%#", self.webview); into both awakeFromNib, and viewDidLoad if you want to see if your web view is nil or not.
During -awakeFromNib, your UIWebview will not be initialized yet. Move your UIWebView code to -viewDidLoad (if you only want it to (re)load once) or -viewDidAppear: (if you want it to reload every time the view becomes active.
I'm tying create a welcome label using NSTimer but its showing the through some warnings
like
undeclared selector hidelable and unused variable timer
I have not used the NSTimer before can one pls tell me where im doing wrong and wt is the right method to do it.I need to give a welcome message when app load after few minities it has to disappear
i have tried this one im not able to get pls help me
this is the code i have to used in the view didload
NSTimer * timer = [NSTimer scheduledTimerWithTimeInterval:60 target:self selector:#selector(hideLabel:) userInfo:nil repeats:NO];
in storyboard i have used ib label which i want to display message
#property (strong, nonatomic) IBOutlet UILabel *wel;
please any one tell wt is proper way to make this one..
you have not declared hideLabel method.hence it gives that warning
[NSTimer scheduledTimerWithTimeInterval:60 target:self selector:#selector(hideLabel:) userInfo:nil repeats:NO];
-(void)hideLabel:(NSTimer *)timer{
myLabel.hidden=YES;
}
Try this
[NSTimer scheduledTimerWithTimeInterval:0.3 target:self selector:#selector(timerCalled) userInfo:nil repeats:YES];
-(void)timerCalled
{
NSLog(#"Timer Called...");
}
According to your code, You didn't use timer anywhere else, and didn't fire repeatedly. Then you can use this code as below..
[self performSelector:#selector(hideLabel:) withObject:yourLabel afterDelay:60];
Importantly, define your target method,
-(void)hideLabel:(UILabel*)label
{
// your code here...
label.text = #"Fired...";
}
If you does not need to use (extra use of) object of NSTimer then you should create NSTimer such like,
[NSTimer scheduledTimerWithTimeInterval:60.0f target:self selector:#selector(hideLabel:) userInfo:nil repeats:NO];
And then you need to declare timer's method other wise after active (60 sec.) timer you will be get error.
- (void)hideLabel:(NSTimer *)theTimer
{
// Timer method code;
}