how to pass argument for gesture selector - objective-c

I added a gesture to a label and when tapped i would like to trigger showlbl that will take int as an argument, however I'm getting a compiler error for:
UITapGestureRecognizer *gestlbl0 = [[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(showlbl:1) ];
thanks!

It doesn't work that way. You can only specify the name of the selector, i.e. the name of the method that is to be called. The form of the selector (the number of arguments) is fixed and defined by the class that calls your action method (in this case, UITapGestureRecognizer).
For this particular action method, the one and only argument to the action method will be an object of type UIGestureRecognizer *). If you the method to have access to another variable, you have to declare an appropriate ivar/property and store the value there.

Related

Call function pointer from selector

I have a NSMenuItem, and I create it using this:
NSMenuItem* nsMenuItem = [[NSMenuItem alloc] initWithTitle:[NSString stringWithUTF8String:menuItem->getText()] action: keyEquivalent:#""];
Now I pass in a function pointer, which I want to call when the selector gets invoked.
How do I do this?
I have tried:
id block = [^{
functionPointer();
} copy];
NSMenuItem* nsMenuItem = [[NSMenuItem alloc] initWithTitle:[NSString stringWithUTF8String:menuItem->getText()] action:#selector(invoke) keyEquivalent:#""];
[nsMenuItem setTarget:block];
However, the menu item is still grayed out.
How do I pass in a function pointer as a selector?
I'm building a cross platform app, and so basically I call to create a new menu, and my core C++ code will pass in a function pointer for the Menu Item.
First, you need to understand how menus work. Did you read the documentation? Especially the section about enabling menu items is very insightful.
To give a short overview: the selector is the name of a function that will be called when the menu item is invoked. It's not a function, it's the name of a function. In Smalltalk one would say it is the message to be send.
The target is the object that is receiving the message.
The Menu mechanism checks if the target implements the selector function. Since your target is a block, it does not implement any function at all, hence the menu item is always disabled.
What you need do is the following:
store the block to be executed somewhere
add a generic selector like "performBlock" to your menu item
set the class that stores the block as a target
in that class, implement a fuction performBlock and call the block there
You could subclass NSMenuItem and keep the block in there, see this example here.

Is it possible to pass a selector or a method signature as a parameter in Objective-C?

As my title states, I'm wondering if I can pass a method signature or an #selector as a parameter? I'm asking because I'm creating a framework and I want to be able to pass instances of a certain class within it a method name.
You can pass the selector itself of use the name of the method as a string:
- (void)myMethod:(SEL)selector
{
[aClass performSelector:selector];
}
or
NSString *myMethodName = NSStringFromSelector(#selector(myMethod));
NSLog(#"The name of the method is: %#", myMethodName);
Actually, you cannot not pass a selector to a method.
In Objective-C, every method gets two implicit arguments, passed as normal parameters: The instance pointer self and the target selector _cmd. They are present in each and every method. The _cmd parameter is of type SEL. It is used by the runtime to look up the method implementation (this is the core of objc's dynamism).
You can, of course, add additional parameter of SEL type.

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.

iOS Set Property After Delay

Is there a way to set a property after a delay? Such as:
// Compiler hates this
[self performSelector:#selector(imageView.image) withObject:[UIImage imageNamed:newImage] afterDelay:0.5];
Or do I have to create a new method to be a called to put the setting of the property in? The above seems so simple.
Yes the default name of the setter for a property named image will be setImage.
[imageView performSelector:#selector(setImage:) withObject:[UIImage imageNamed:newImage] afterDelay:0.5];
When you do not set the setter method it will generate a method prefixed with set then camel cased and it will accept a parameter of its type.
Ex.
#property(assign) id object;
//becomes
-(id)object {...} // getter
-(void)setObject:(id)inObject {...} //setter
Alternatively, use dispatch_after. If you type dispatch_after and ask for code completion, you get three lines of boilerplate code and a place to put your actual code in a block. In that block, imageView.image= is legal. Don't forget to change the default delay time from 2 seconds (kind of a long delay!).

Accessing element in array - Request for member in something not a struture or union

I have created a custom button called ModuleUIButton which inherits from UIButton. This custom button has an mutable array property called cycleList.
I have an mutable array of ModuleUIButton's called moduleList. I need to be able to access an element of the cycleList mutable array, and I use the following call:
//Create ModuleUIButton
ModuleUIButton *newModuleButton = [[ModuleUIButton alloc]
//Customize ModuleUIButton
initWithFrame:CGRectMake(29.0, (200.0+(88*moduleCounter)), 692, 80.0)];
[newModuleButton setTitle:#"Module" forState:UIControlStateNormal];
[newModuleButton setBackgroundColor:[UIColor purpleColor]];
//Add newModuleButton to moduleList array
[moduleList addObject:newModuleButton];
//Access the first element in moduleList which is newModuleButton and add
// a string to it's cycleList property
[[self.moduleList objectAtIndex:0].cycleList addObject:#"new Cycle"];
When attempting to compile however, I get:
Request for member 'cycleList' in something not a structure or a Union
Is the compiler complaining because it doesnt know that moduleList's 0th element will be a ModuleUIButton? If so, how can I access any of the ModuleUIButtons properties by referencing it from the mutable array?
Any insight would be appreciated!
Is the compiler complaining because it doesnt know that moduleList's 0th element will be a ModuleUIButton?
Exactly. The return type of objectAtIndex: is id. The compiler can't know what the actual type is, and can't therefore turn the property access .cycleList into the correct method call as it normally would, because it can't know what the correct method is.
The property access essentially works by rewriting your code. The complier sees foo.bar and goes to foo's class, finds the methods that correspond to the bar property, and turns what you wrote into [foo bar] or [foo setBar:], as appropriate. The trick is that, since the methods associated with a property can have any name (not just the standard bar/setBar) the compiler must be able to determine the type of the object in order to figure out the right method names to use.
When you use the bracket syntax, you're telling the compiler the exact name of the method you want called. It doesn't have to do any lookup, it just turns that into the usual call to objc_msgSend.
If so, how can I access any of the ModuleUIButton's properties by referencing it from the mutable array?
Just use the "standard" syntax:
[[self.moduleList objectAtIndex:0] cycleList]
You can also give the compiler a hint by casting, as Taskinoor suggested:
((ModuleUIButton *)[self.moduleList objectAtIndex:0]).cycleList
This explicitly tells the compiler to treat the object being returned from objectAtIndex: as a ModuleUIButton; it can then figure out what the method name should be.
Try to cast like this:
ModuleUIButton *button = (ModuleUIButton *)[self.moduleList objectAtIndex:0];
[button.cycleList addObject:#"new Cycle"];