How to addTarget with more properties? - objective-c

one simple question cause I couldn't find quickly any special answer for my issue.
I have such a method:
- (void)changeValueForEasy:(UISlider *)slider offset:(int)offset {}
And I'm trying to call it with event, but I don't know how to add an additional object like "offset" to this:
[blueSlider addTarget:self action:#selector(changeValueForEasy:) forControlEvents:UIControlEventValueChanged];

Maybe something like this
[blueSlider addTarget:self action:#selector(sliderDragged:) forControlEvents:UIControlEventValueChanged];
then
- (void)sliderDragged:(UISlider *)slider;
{
[self changeValueForEasy:slider offset:slider.value];
}
The method name suggests that it sets the slider's value, the fact that you want to call this method suggests that you do some extra processing when the value is changed. I would recommend abstracting the additional processing out into another method or the slider's value will be being set twice.

Related

Can I use a normal function, not a method selector, as an event handler in Objective C?

I'm really bad at objective C, and I need to write this one button click handler. Currently I have this:
NSButton *button = ...
[button setAction:buttonPressed];
// This also doesn't work
[button setAction:#selector(buttonPressed)];
void buttonPressed() { NSLog(#"Button pressed!"); }
As I understand, setAction() requires a selector which is kind of like a method pointer. I don't have any classes here, so no methods. Is there a way to use a simple function for the button click event?
Thanks
No, you cannot just pass some (reference to a) plain C function to something that expects an Objective-C method selector. Of course, you can wrap your method up in some class and call your native method from there.
A simplified explanation is that the method will be called "on some object"; that's just how the framework works here.

How does (id)sender work in cocos2d?

The following code works good for me:
In the init method of a menu layer:
CCMenuItemFont *item1 = [CCMenuItemFont itemWithString:#"Level 1" target: self selector: #selector(startLevel:)];
item1.userData = (__bridge void*) ([NSNumber numberWithInt:1]);
...//create menu and add in the item1
-(void)startLevel: (CCMenuItem *)sender
{
NSNumber *number = sender.userData;
...
}
My questions are:
I didn't pass item1 when call the method startLevel: how does it know that the sender is item1?
is it written into selector? or is it written in cocoa?
CCMenuItem passes itself as parameter to this selector. Details are in CCMenuItem source code.
Regarding omitting passing itself as a parameter, do you mean like...
- (void) pushedStart : (id) sender
{
//start game
}
but you can't do
[self pushedStart];
because it needs a parameter? If so, what you can do this:
id junkID;
[self pushedStart: junkID];
JunkID will initialize to whatever the hell it is an unassigned ID assigns to, so you pass it as a reference and just don't use it for anything, which is good if you want to have a "start game" button but have the game automatically start inside of a timer or whatever else you're doing with your buttons
As a side note, and getting more into the guts of cocoa, the way it KNOWS (and what YOU must not forget) is that colon. When you call a function you put the variable after a colon [self eat: food];
When you put together the menu item you set it up with target:self, which makes the button use itself (not the layer "self" you use when you call [self eatABanana]) as a target. The button push of
menuButton = target:self selector:#selector(pushButton:)
is represented like
[self pushButton:menuButton]
If you forgot that colon, it's the same as calling a function and not passing the variable, which doesn't give a very helpful error message insofar as it doesn't help you locate where the problem is occurring. I've spent hours upon hours chasing down memory crashes resulting from writing #selector(startGame) instead of #selector(startGame:) in those damn menu buttons. I always feel stupid when I finally figure it out.

How to pass parameters using #selector

I need to pass parameters with #selector and here is the method that i need to call using selector:
-(void)clickedInfo:(NSString *)itemIndex{
// some work with itemIndex
}
I know that what i can do is to use an intermediate method as described here.
This approach doesn't work in my case because im adding the target to the uibutton in the cellForItemAtIndexPath method for the collectionView.
The parameter that i need to pass to the clickedInfo method is indexPath.row
and i can not obtain this parameter in an intermediate method.
Thanx in advance
So you want to store some information that can be accessed by the action of a button. Some options are:
Use the tag property of the control. (can only store an integer)
Subclass UIButton and use that class for the button. The class can have a field that stores the information.
Use associated objects (associative references) to attach an object to the button. This is the most general solution.
You can use the performSelector:withObject: selector to pass an object.
Example:
[self performSelector:#selector(clickedInfo:) withObject:myIndex];
- (void) clickedInfo:(NSString *)itemIndex{
// some work with itemIndex
}
Edit: Should be just #selector(clickedInfo:) rather than what I had before.
Edit: Using #newacct 's suggestion, I'd recommend doing something similar to the following:
- (UITableViewCell *)tableView:(UITableView)tableView cellForRowAtIndexPath:(NSIndexPath)indexPath
{
button.tag = indexPath.row;
[button performSelector:#selector(clickedInfo:)];
// or
[button addTarget:self action:#selector(clickedInfo:) forControlEvents:UITouchUpInside];
}
- (void) clickedInfo:(id)sender
{
int row = sender.tag;
// Do stuff with the button and data
}
this is addressed lots of places, but it is easier to answer than to point you there:
[someObject performSelector:#selector(clickedInfo:) withObject:someOtherObject];
where someObject is the receiver and someOtherObject is the parameter passed to clickedInfo

passing 2 arguments for #selector in its simplest way

SEL twoArgumentSelector = #selector(methodWithTwoArguments:and:);
[newsButton addTarget:self action:#selector(twoArgumentSelector) forControlEvents:UIControlEventTouchUpInside];
-(void)methodWIthTwoArguments:(id)argumentOne and:(id)argumentTwo;
I have seen some examples that let you use two arguments in a selector. How would any do that in the above code? ty in advance.
#selector(twoArgumentSelector:and:)
although I'm not sure how you would send two arguments with a control event...
edit:
you know that the selector isn't actually calling the method, so you can't pass the arguments with the selector. It is basically just the name for a block of code (the method). Read this. A better solution would be to have the control event call a separate method which could then determine the arguments to send to the method with 2 parameters.
UIControls events by will only send a reference to themselves if their target selector allows for one argument. This is all you get. UIButton is one such UIControl subclass.
- (void)buttonAction:(id)sender; //(reference to button)
The easiest way to accomplish what you want is to make another method on your button target (in this case self) that calls out to your two argument selector.
[newsButton addTarget:self action:#selector(buttonAction:) forControlEvents:...];
- (void)buttonAction:(id)sender
{
[self methodwithTwoArguements:sender and:otherObject];
}
This could also be solved with a UIButton subclass, but depending on what your second argument needs to be, this is the simplest way.
Unless you are subclassing the object, there is no need for this because the argument to UIControlEvents is the id of the instance sending the event. You can get all the information you could possibly need from that instance.
What you're looking for is NSInvocation.
Here's an example:
SEL mySelector; // 2 parameter selector.
// ...
NSMethodSignature *signature = [[MyClass class] instanceMethodSignatureForSelector:mySelector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setTarget:myTarget];
[invocation setArgument:arg1 atIndex:0];
[invocation setArgument:arg2 atIndex:1];
[invocation invoke];
This will do exactly what you want.
It's worth noting, however, that the control event you are binding to isn't going to pass anything in for a second parameter. This will only work if, by contract, the object that is calling back actually has a second parameter to pass in.

How to make a periodic call to a method in objective c?

if the question is not explained clearly please excuse me.
I'm developing an iphone Client-Server app, i created all the classes, instances and ect. I can even send get and parse the response too.. Anyway, now i need to make my method be called in a defined period of time(for instance, call it repeatly in 10 seconds). I googled a lot and also take a look at
NSDate but i couldn't solve.. Now, can anyone please help me how to handle this situation? Thank you very much
You can create and schedule an instance of NSTimer to call your method on a given time interval. See in particular the following convenience creation method:
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)seconds target:(id)target selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)repeats
http://developer.apple.com/library/ios/#documentation/cocoa/reference/foundation/Classes/NSTimer_Class/Reference/NSTimer.html
Your best bet is to look into Grand Central Dispatch since you are going to want to run this in the background:
Use NSTimer mentioned above.
What you want is the NSObject method -performSelector:withObject:afterDelay:.
Click for docs.
If you have the method that it calls call this on itself again, you'll have a looping, self-firing delayed poll method.
I'd recommend checking a class variable to see if you really mean it each time, so you can turn it off from outside itself.
Grand Central Dispatch will create a different thread to run on. so if the timer method (shown below and suggested above) lags your app you will need to put the command on a separate thread.
NSTimer is what you should use though. for example if you want to repeat a method that is initiated from a button press you could do this
- (void)viewDidLoad
{
[super viewDidLoad];
[cameraControlButtonUp addTarget:self action:#selector(cameraControlButtonUpPressed)
forControlEvents:UIControlEventTouchUpInside];
}
-(IBAction)buttonDown:(id)sender{
NSInteger tag = [sender tag];
if (tag==1) {
buttonCounter=1;
timer = [[NSTimer scheduledTimerWithTimeInterval:.5 target:self selector:#selector(sendJoin) userInfo:nil repeats:YES]retain];
}
}
-(void)sendJoin
{
switch (buttonCounter) {
case 1:
[cClient userDigitalPushAndRelease:372];
break;
default:
break;
}
}
-(void)cameraControlButtonUpPressed
{
[timer invalidate];
}
that will repeat the command till the button is released. take in mind you need to link the ibaction with the button down event (only the button down event). as well as create timer in the .h and tag the button to 1 that you want to use this with.
for a more basic example; its quite simple. just create your method to call, timer and set repeat to YES. then call invalidate to stop it. i had to create a seperate method sendJoin because i couldnt get the numbers to pass correctly to the method. but if you dont have any parameters its even easier. just use the timer syntax to create it then invalidate it when ur done