Passing arguments to selector that is an argument - ios

I have the following method
-(void)changeLevelWithTimeInterval:(NSTimeInterval)timeInterval withLevel:(NSUInteger)level{
[NSTimer scheduledTimerWithTimeInterval:timeInterval
target:self
selector:#selector(changeLevel:)
userInfo:nil
repeats:NO];
}
and I want to call the changeLevel:(NSUInteger)level method with the level argument of the method above.
I have tried to put
[self performSelector:#selector(changeLevel:) withObject:level]
and tried to pass this as an argument but it doesn't work.

performSelector:withObject: takes a pointer object (aka an id) as an argument, so there is no way to pass in an NSUInteger as an argument, as NSUInteger is just a typedeffed unsigned int. One particular workaround is to encapsulate the level in an NSNumber object, and get the intValue of the NSNumber in the changeLevel: method.
It may go as:
NSNumber *levelNumber = [NSNumber numberWithInt:(int)level];
[self performSelector:#selector(changeLevel:) withObject:levelNumber];
And change the signature and implementation of changeLevel: as follows:
-(void)changeLevel:(NSNumber*)levelNumber{
int level = [levelNumber intValue];
//do anything you want with the level..
}
For firing the method after an interval, you can try:
[self performSelector:#selector(changeLevel:) withObject:levelNumber afterDelay:timeInterval];
Instead of scheduling a timer directly.

I ended up using the following method instead of the method above to make everything work correctly.
-(void)changeLevelWithTimeInterval:(NSTimeInterval)timeInterval withLevel:(NSUInteger)level{
NSNumber *levelNumber = [NSNumber numberWithInt:(int)level];
[self performSelector:#selector(changeLevel:) withObject:levelNumber afterDelay:timeInterval];
}

Related

pass info from Model to viewController without NSNotification

In my viewController, I have a button that calls a method to start a timer in a Timer class. Just like this
main view controller
[self.timer startTimer];
In the timer class, startTimer calls a countdownTime method, both of which you can see below. Very simple. At the end of the countdownTime, method, the time is put in the label in the view like this as I iterate through a loop of the clocks.
Timer *timerblah = self.gameClocks[i];
[self.gameClocks[i] setText: [NSString stringWithFormat:#"%#", self.time]];
In other words, the Timer class has an array property that holds all the clocks and that is connected to the view, hence the ability to set the text.
The problem with this code is that the model (i.e. Timer class) is setting the text in the view. I want the view Controller to get the time from the model and have the text set in the view controller i.e. have the viewController and only the viewController communicate with the view. It is no problem for me to get the array of clocks in the viewController, however, I'm sure how to pass the time back from the countdownTime method to the viewController. It would seem like overkill to set up an NSNotification class to send the time back every second, wouldn't it?
-(void)startTimer{
self.NStimer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(countdownTime:)
userInfo:nil
repeats:YES];
}
-(void)countdownTime:(NSTimer *)timer
{
#logic to countdown time
self.minutes = self.secondsRemaining / 60;
self.stringMinutes = [NSString stringWithFormat:#"%i", self.minutes];
self.seconds = self.secondsRemaining - (self.minutes * 60);
self.stringSeconds = [NSString stringWithFormat:#"%i", self.seconds];
if (self.seconds < 10) self.stringSeconds = [NSString stringWithFormat:#"0%i", self.seconds];
self.time = [NSString stringWithFormat:#"%#:%#", self.stringMinutes, self.stringSeconds];
self.secondsRemaining -= 1;
if (self.secondsRemaining == 0){
[self stopTimer];
}
#adding to the view (from the model i.e. in Timer.m class)
for(int i = 0; i < [self.gameClocks count]; i++){
Timer *timerblah = self.gameClocks[i];
if (timerblah.systemClock == YES){
[self.gameClocks[i] setText: [NSString stringWithFormat:#"%#", self.time]];
}
}
}
Update
There's a comment suggesting that I use a block to do this. In the timer class, I added a method like this to return a string with the time (the time is set to self.time above)
-(NSString *)passTheTime:(NSString (^)(void))returnBlock
{
return self.time;
}
I then, in view controller, call the method passTheTime on the timer class. I created a property in the view controller that will store the time, so I pass that as a parameter to the block,
[self.timer passTheFoo:^NSString * (self.timeToSet){
}];
a) I'm unsure of what to do in the block here.
b) I'm unsure of whether it was necessary to pass self.timeToSet into the block. How do I connect self.time from the Timer class to self.timeToSet in the view controller
c) there's an error incompatible block pointer type sending NSString *((^)(void)) to parameter of type NSString(^)(void)
d) alternatively, can I pass a block to startTimer, and have that block passed in the selector countdownTime and return the self.time once the calculations in countdownTime are finished?
You can use block to update the label in the view controller class as below
In Timer Class, create a block
TimerClass.h
#import <Foundation/Foundation.h>
typedef void(^TimerCallback)(NSString *time);
#interface TimerClass : NSObject{
TimerCallback timerCallback;
NSTimer *timer;
}
- (void)startTimerWithCallBack:(TimerCallback)callback;
#end
In TimerClass.m , update the code as below
#import "TimerClass.h"
#implementation TimerClass
- (void)startTimerWithCallBack:(TimerCallback)callback{
timerCallback = callback;
timer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(countdownTime:)
userInfo:nil
repeats:YES];
}
-(void)countdownTime:(NSTimer *)timer
{
// countdown logic here
// call the callback method with time as a parameter to the viewcontroller class
// here for demo purpose I am passing random number as a value in callback, you need to pass the time that you want to display here
NSString *str = [NSString stringWithFormat:#"%d",1+arc4random()%10];
timerCallback(str);
}
#end
In ViewController class, call the timer start method as follows
TimerClass *timer = [[TimerClass alloc] init];
[timer startTimerWithCallBack:^(NSString *time) {
lbl.text = time; // display time in label
}];

NSTimer stops after incrementing once

I have an NSTimer which I want to update a label every second. My code is:
- (IBAction)OnClickEmergencyButton:(id)sender
{
emergencyAlertTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(emergencyTimer) userInfo:nil repeats:YES];
[emergencyAlertTimer fire];
}
- (void)emergencyTimer
{
int i = 0;
_emergencyAlertTriggerTimerLabel.text = [NSString stringWithFormat:#"%d", ++i];
}
When I ran it, the label displayed "1" initially and then stopped.
I want the label to continuously count up every second, like "1", "2", "3", ...
There is no issue with your timer. The issue is with the variable declaration inside the emergencyTimer, you declared it as a local variable. So each time when the timer fires the variable will be initialized to 0 again. So declare the variable as static, so that it can preserve the value.
Change the method like:
-(void)emergencyTimer
{
static int timeValue = 0;
_emergencyAlertTriggerTimerLabel.text = [NSString stringWithFormat:#"%d",++timeValue];
}
Why static variable and Why not instance variable ?
I didn't used instance variable, for keeping the variable "Scope" safe. If I put it as an instance variable, it can be accessed by other methods of the same class, if there is no need of that functionality, I think using a static variable will be better.
Issue is with this code
int i=0;
Every time when timer method gets called, the integer i gets initialized and label will be displayed as "1" always.
Make this variable global or static to fix your issue.
Remove int i=0; from your timer action method because it will always have a value of zero. You should be using an instance variable (#property) to store the timerCounter and increment that and use it to populate the label.
At some point in time you need to invalidate the timer. This is particularly important to do before you create a new timer and replace the reference to the old timer. Currently, if you press the button twice, you will then have 2 timers running and your label will increment twice a second...
#property (nonatomic, assign) NSInteger timerCounter;
- (IBAction)OnClickEmergencyButton:(id)sender {
[emergencyAlertTimer invalidate];
emergencyAlertTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(emergencyTimer) userInfo:nil repeats:YES];
[emergencyAlertTimer fire];
}
- (void)emergencyTimer {
_emergencyAlertTriggerTimerLabel.text = [NSString stringWithFormat:#"%d", self.timerCounter];
self.timerCounter++;
}
You should also invalidate the timer when the view is removed from display / deallocated.
Always timer get called emergencyTimer but your value of i won't changed because it's an local variable, scope of i will remain at end of function call. Try this with static variable which remain globally...
-(void)emergencyTimer{
static int i=0; // initialize at first time only..
_emergencyAlertTriggerTimerLabel.text = [NSString stringWithFormat:#"%d",++i];
if ( i == 100)
[ emergencyAlertTimer invalidate] // stop at certain condition
}
Firstly everyone is right you will only display a 0 no matter what you do currently so use an instance variable.
With regards to the only firing once, instead of [NSTimer fire] try this:
emergencyAlertTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(emergencyTimer) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:emergencyAlertTimer forMode:NSRunLoopCommonModes];

new to ios - unrecognized selector sent to instance message

here is my code:
-(void)flash_random_winning_number:(NSInteger)param_which_magic_number
{
NSInteger dd = 9;
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:.5
target:self
selector:#selector(ShowLabel22:dd:)
userInfo:Nil
repeats: YES
];
}//flash_random_winning_number
the problem is with selector:#selector(ShowLabel22:dd:) because I am sending a parameter to this method called ShowLabel22:
-(void)ShowLabel:(NSInteger)param_which_magic_number
{
random_magic_number1.hidden = NO;
}
however if I were to remove the parameters from all this code, then there is no error. Therefore it seems as if I have mistake in the way I am using parameters.
The selector you pass to scheduledTimerWithTimeInterval:target:selector:userInfo:repeats: is akin to the name of a function. What you’re trying to do is to pass in the name of a function and the value of its parameters, but that won’t work. The method you name using #selector(...) will be passed exactly one argument: the NSTimer object calling the method.
If you have data you need to make available to ShowLabel it needs to be attached to the timer object or to self or something like that. To attach your number to the timer object you could do this:
- (void) flash_random_winning_number:(NSInteger) param_which_magic_number
{
NSInteger dd = 9;
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:.5
target:self
selector:#selector(ShowLabel:)
userInfo:#(dd)
repeats:YES];
}
- (void) ShowLabel:(NSTimer *)timer
{
NSInteger dd = [[timer userInfo] integerValue];
/* do stuff with dd */
}
(Since you’re new to Objective-C I want to mention the conventions that have arisen about method naming. The usual style, which you should always use, is for methods to be named with camelCasedNamesLikeThis. In particular, flash_random_winning_number and ShowLabel are both likely to confuse other Objective-C developers because they don’t “look like” method names. ShowLabel22 is also weird because of the numbers, but since you included that in one place in your question but not another I guess that’s just a typo.)
Look at this SO answer. When you have a selector with arguments you have to use this method:
[self performSelector:#selector(myTest:) withObject:myString];

Objective-C: Accessing integer value of a ViewController in View?

I'm trying to start a NSTimer in my UIView class called "ClockView" with a method as selector that manipulates an initial float which was declared in the ViewController "ClockViewController".
My ClockViewController declares int timerIntWhite as an integer (for example 500). My ClockView needs this Value for the - (void)start method which runs a method called - (void)updateWhiteClock every second:
- (void)start {
timerIntWhite = PLEASE HELP ME AT THIS POINT!;
randomTimerWhite = [NSTimer scheduledTimerWithTimeInterval:(1.0/1.0)target:self selector:#selector(updateWhiteClock) userInfo:nil repeats:YES];
}
Is it possible to access the integer of ClockViewController in ClockView?
Try the following:
In your ClockView also declare the variable (property) int timerIntWhite and set this variable from your View Controller after the View gets created, for example, in viewDidLoad.
-(void)viewDidLoad {
[super viewDidLoad];
self.view.timerIntWhite = self.timerIntWhite;
}
After doing this, ClockView can access it's own timerIntWhite variable:
- (void)start {
timerIntWhite = self.timerIntWhite;
randomTimerWhite = [NSTimer scheduledTimerWithTimeInterval:(1.0/1.0)target:self selector:#selector(updateWhiteClock) userInfo:nil repeats:YES];
}
I'm assuming that your ClockViewController class knows that its view IS-A ClockView. This is very important! Otherwise you'll get a warning.
I also want to mention that according to the MVC rules it's a better idea if your ClockViewController class takes care of the NSTimer. Views should be used to display information to the user only.
Hope this helps!

passing float variable as parameter

I am trying to write a method with float parameter and call it using performSelector` but I am getting error in doing this. Following is my code:
[sender performSelector:selector withObject:progress/total];
Here progress and total both are float variable.
I am trying to call following method in different class:
-(void) updateProgress:(float)fl {
}
You need to pass a real object, not one of the basic types like int or float.
Wrap it into an NSNumber object:
[sender performSelector:selector withObject:[NSNumber numberWithFloat:progress/total]];
-(void) updateProgress:(NSNumber *)aProgress {
float fProgress = [aProgress floatValue];
}
It's because -performSelector:withObject: only works for Objective-C objects. float isn't one of these.
Why not just use
[(TheClass*)sender updateProgress:progress/total];
?

Resources