I dont know if this is possible... I have this code in touchesEnded:
[self performSelector:#selector(GameOver) withObject:nil afterDelay:3];
However, if you touch the screen again before the 3 seconds is up, I want to cancel the calling of GameOver. Any idea how I can do this.
In touchesBegan, I tried something like this:
[NSObject cancelPreviousPerformRequestsWithTarget:self
selector:#selector(touchesEnded:withEvent:)
object:nil];
This did not work.
Your selector value is wrong in your cancelPreviousPerformRequestsWithTarget call, that's all. If the original selector in performSelector was GameOver then obviously the perform request you are canceling must be specified as GameOver.
This is perfectly clear from the documentation: https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSObject_Class/index.html#//apple_ref/occ/clm/NSObject/cancelPreviousPerformRequestsWithTarget:selector:object:
Related
I am repeating a function again and again using this code
- (void)refresh {
[self performSelector:#selector(refresh) withObject:nil afterDelay:5.0];
}
On viewWillDisappear I wrote the code to cancel this but still the function called. How can we cancel this perform selector repeating itself ?
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(refresh) object:nil];
}
please First of all , check this you might be running on different run loops in creation and cancellation.I faced this problem in past.
or
I think no need to write selector again. Just right this and try.
With this
// cancel the above call (and any others on self)
[NSObject cancelPreviousPerformRequestsWithTarget:self];
Refer apples document.
NSObject_Class
Discussion All perform requests are canceled that have the same target
as aTarget, argument as anArgument, and selector as aSelector. This
method removes perform requests only in the current run loop, not all
run loops.
The server that provides data to my app recently added a feature that allows you do to basic logging like "user selected logo" or "user is quitting".
The only place I'd like to use this is in a page with several sliders that does a calculation on the input values. This is continuous, it re-calculates the output as you move the sliders around.
Which leaves me the problem of when to call this logging method. I don't want to call it every time the numbers change, or I'll murder the server. I could put a "Calculate now" button, but that kills the entire mode-less UI I like.
Is there a way that I can coalesce calls so all the calls made within, say, 5 seconds, results in only one call to the work method? I'd also have to force the method to fire if the user does something else, like navigates away or quits the app.
You can easily add an NSTimer to the IBAction method you have for your slider. Every time that method is called, invalidate the timer and start it again. Put the analytics call in the timer's action method, which will only be called when the timer can actually complete.
For example:
#interface ViewController ()
#property (nonatomic) NSTimer *actionTimer;
#end
#implementation ViewController
- (IBAction)sliderChanged:(UISlider *)sender
{
[self.actionTimer invalidate];
NSLog(#"Slider value: %f", sender.value);
self.actionTimer = [NSTimer scheduledTimerWithTimeInterval:2
target:self
selector:#selector(timerCompleted)
userInfo:nil
repeats:NO];
}
- (void)timerCompleted
{
NSLog(#"Timer completed.");
}
#end
How about a background thread that observes the calculated value and fires an update by scheduling a block on a background thread when the output value is stable, like
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(showValue:)
name:#"showValue"
object:nil];
You can also schedule the block in viewWillDisappear for the leaving the view and (I think) quit events.
I am working on a project where I there is some text in UItextview. The app wants to continuous smooth scroll that text and also wants to manage its scrolling speed. I mean here the text should scroll smoothly and the app contains slider where I can manage the speed.
Below is some sample code which I am using.
- (void) autoscrollTimerFired:(NSTimer *)timer {
[self.completeText setContentOffset:CGPointMake(0, self.completeText.contentOffset.y + 1.0) animated:NO];
if (self.completeText.contentOffset.y != self.completeText.contentSize.height - self.completeText.frame.size.height) {
scrollingTimer = [NSTimer scheduledTimerWithTimeInterval:velocityFactor target:self selector:#selector(autoscrollTimerFired:) userInfo:nil repeats:NO];
} else {
[scrollingTimer invalidate];
}
}
The Velocity factor is the number of seconds which ranges between 0.0 to 2.5.
It works nice in simulator but in device it moves with jerks or I must say like pausing at after some line.
Could you please suggest any solution here? All suggestions are welcome.
The NSTimers actually just periodically fire events into the enclosing NSRunLoop, which each thread has (or should have). So, if you have a child (or background) process running in a different thread, the NSTimers will fire against that thread's NSRunLoop instead of the application's main NSRunLoop.
NSTimer events fire on the thread where you scheduled the timer. If you scheduled the timer on the main run loop, then the timer will fire on the main thread and be “safe and synced” with input events.
So I have some suggestions for try that (I am not sure how much it will be successful in your case)
Don't use NSTimer. Try to call from main thread via selector with "afterDelay". As given in code
[self performSelector:#selector(autoScroll) withObject:nil afterDelay:1.0];
Use KVO or may be NSNotification for event triggering. (Not do it directly ,I Think better approach.)
I prefers KVO , so writing here the steps to use it :-
Make a class with a variable of NSNumber (which should accessible outside the class). Make a object of that class and add a Observer on it. (here model is object of that class).
[model addObserver:self forKeyPath:#"name" options:(NSKeyValueObservingOptionOld |
NSKeyValueObservingOptionNew) context:nil];
Implements it's delegate methods.
-(void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
// Do here what ever you want to do. It will call every time whenever there will be any change in that class variable.
}
Make a method in controller "autoScroll" which called via afterDelay selector. And change the value of the NSNumber vairabe value (any logic incremental way. Doesn't affect a lot).
Hope this helps you !!! Try This ...best of luck
I want to send an NSNotification from this method (when the UIButton is clicked) in my AppDelegate.m:
- (void)alertView:(UIAlertView *)alertView
clickedButtonAtIndex:(NSInteger)buttonIndex{
if (buttonIndex == 0){
//cancel clicked ...do your action
// HERE
}
}
..and receive it in one of my UIViewControllers. How can I do that?
EDIT WITH MORE INFO: I am making an alarm app, and when the user presses the UIButton, I want to stop the alarm. I figured that NSNotifications is the only way to get information from my AppDelegate.m file to a ViewController.m file?
You should to register your receiver object to accept some messages sent from Notification Center.
Suppose you have Obj A which controls your alarm, and value "stopAlarm" is the message which can stop alarm. You should create an observer for a "stopAlarm" message.
You can do, with:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(controller:)
name:#"stopAlarm"
object:nil];
Now, you should create a method controller that manages this messages:
- (void)controller:(NSNotification *) notification {
if ([[notification name] isEqualToString:#"stopAlarm"]){
//Perform stop alarm with A object
}
}
Finally, you can send the message "stopAlarm" when you want in the code with:
[[NSNotificationCenter defaultCenter]
postNotificationName:#"stopAlarm"
object:nil];
I Hope this may help.
EDIT:
When your UIViewController are unloaded or when app terminate, you should call:
[[NSNotificationCenter defaultCenter] removeObserver:self];
for stop observing.
That's all.
Thanks to Hot licks for correction.
You may want to create a class–maybe even a singleton if there is only one alarm–that manages the timer. That way you can manage from any place in your application rather than in the view controller. Take a look at:
http://dadabeatnik.wordpress.com/2013/07/28/objective-c-singletons-an-alternative-pattern/
and:
https://developer.apple.com/library/mac/documentation/cocoa/conceptual/Notifications/Articles/NotificationCenters.html
As Hot Licks mentioned, you really do not want to jump into this without knowing what is going on. Hopefully these links will help get you going in the right direction.
I am using a UIWebView and don't want the navigation bar to appear unless the user taps anywhere on the screen that isn't a link.
So I have this code to display the navigation bar after a delay:
- (void)handleTapGesture:(UITapGestureRecognizer *)sender
{
....
[self performSelector:#selector(showNavigationBar) withObject:self afterDelay:0.2];
}
I'm not calling showNavigationBar immediately when the tap handler is invoked because the user might have tapped on a link in which case the tap hander is called before UIWebView shouldStartLoadWithRequest, so if I hid the navigation bar in shouldStartLoadWithRequest it would flash momentarily onto the screen.
So instead I set it to display after a delay which gives time for the following code to execute within shouldStartLoadWithRequest (and if the user didn't tap on a link shouldStartLoadWithRequest isn't called and the navigation bar is displayed, as it should be in that case).
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(showNavigationBar) object:nil];
...
However this isn't working, I've increased the delay time to several seconds and can confirm cancelPreviousPerformRequestWithTarget is getting called before the navigation bar has been displayed, but when the specified time elapses the bar displays. cancelPreviousPerformRequestWithTarget is having no effect.
Does anybody know why its not working?
Your perform doesn't match your cancel. In the perform you're passing self as the object:
[self performSelector:#selector(showNavigationBar) withObject:self afterDelay:0.2];
In the cancel you're passing nil as the object:
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(showNavigationBar) object:nil];
They don't match, so the delayed perform should not be canceled.
In the documentation of that + (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget selector:(SEL)aSelector object:(id)anArgument method there is this sentence :
This method removes perform requests only in the current run loop, not all run loops.
If I'm interpreting it correctly it would mean that you need to cancel your action in the same run loop that you launched it. Which is clearly not what you want to do.
A way to go around this would be to have a flag that showNavigationBar would have to check to see if it should proceed or abort.
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(showNavigationBar) object:self];
That worked for me ;)
Not sure why but works like a charm for me.
dispatch_async(dispatch_get_main_queue(), ^{
[NSObject cancelPreviousPerformRequestsWithTarget:self];
});