#selector key word in iPhone programming - iphone

-(void)displayNameBy:(NSString*)name{
mylable.text = name;
}
i want call this method using #selector keywords.
eg:
[MyButton addTarget:self action:***#selector(displayNameBy:name)*** forControlEvents:UIControlEventTouchUpInside];
Here bold italics is my doubt.. could i pass name parameter from here. when i try to pass name value i getting error.
any way to get name value in the displayNameBy:name method . using #selector key words.
here MyButton i created by programatically . not in interface builder.
thanks and regards

Have an intermediate method.
like:
{
...
[MyButton addTarget:self action:#selector(displayName) forControlEvents:UIControlEventTouchUpInside];
...
}
-(void) displayName
{
[self performSelector:#selector(displayNameBy:) withObject:#"name"];
}
-(void)displayNameBy:(NSString*)name{
mylable.text = name;
}

If you create a one-argument selector for your action (like you're doing in displayNameBy:), the argument is the sender of the action. In this case, your button instance.
According to the UIControl docs, there are three different types of supported selectors:
- (void)action
- (void)action:(id)sender
- (void)action:(id)sender forEvent:(UIEvent *)event
Where are you expecting name to be defined? Is it based on the user's actions, the app state, or something else? Depending on what method is used, you could create a UIButton subclass that includes either the necessary logic or declares a delegate protocol, set the delegate to your view controller, and have the delegate implementation set the button's label. The latter is probably better from a MVC standpoint, unless the source of name is blindingly simply (ie, an attribute that could be set on the button, not something the button would have to calculate based on other application state).

The method's selector is just displayNameBy:. The name it the end is the name of the parameter. However, I don't know where you're expecting this NSString *name parameter to come from. The argument for an action method is the sender, which will be the button in this case. So it would be - (void)displayNameBy:(id)sender.
If you're trying to pass the parameter through the selector, that isn't possible. A selector is literally just a name — it doesn't specify any particular behavior.
If you wanted to use PLBlocks, you could create a trampoline class that would be called like:
[myButton setTarget:[BlockProxy proxyWithBlock:^{ [self displayNameBy:name]; }] action:#selector(call:) forControlEvents:UIControlEventTouchUpInside];
That's the closest you'll get, I think. Because what you really want is for the button to call a closure, which is what PLBlocks gives you. Whether it's worth the trouble to get this kind of expressiveness is you're call.

Related

Return customized UIButton in method?

I have a method that takes a UIButton, modifies its properties and returns a UIButton. However, it doesn't ever seem to be initialized. I'm sure I'm doing something wrong with the memory management here, but don't exactly know how to fix it. No runtime errors occur.
It is called like so...
newGameBtn = [self customButtonFromButton:newGameBtn
withText:[NSString stringWithFormat:#"NEW GAME"]
withFontSize:22
withBorderColor:[UIColor orangeColor]
isSilent:YES];
[dashboardContainer addSubview:newGameBtn];
The method is defined as follows...
- (UIButton*) customButtonFromButton:(UIButton*)button withText:(NSString*)text withFontSize:(int)fontSize withBorderColor:(UIColor*)borderColor isSilent:(BOOL)isSilent {
button = [[[UIButton alloc] init] autorelease];
// Set properties from parameters
// Other conditional custom stuff here
return button;
}
NOTE: newGameBtn is of type UIButton* and is initialized with:
newGameBtn = [[UIButton buttonWithType:UIButtonTypeCustom] retain];
Another option might be to subclass UIButton, but I figured I'd try to fix this since I've already walked down this path.
You should use +[UIButton buttonWithType:] when creating buttons to get a properly initialized button.
Most classes are not properly initialized by the default -[NSObject init] method. So please look at the class reference, or superclass reference, for a usable initialization method.
In this case you should also set a frame.
You don't modify this button with your method, you're creating a completely new one with alloc-init!
If you want to change the button in your first argument just remove the first line of your method

How to get the name of the button in an action?

I have a button named start and I want to know in the method that it calls what it's name is and I'm not really sure how to do it. This is the method the button calls.
-(IBAction) startMotion: (id)sender {
UIButton * buttonName = (UIButton *) sender;
NSLog(#"Button Name: %#", buttonName.currentTitle);
}
The NSLog prints
Button Name: (null)
You can set the title of the button through
[b setTitle:#"Start" forState:UIControlStateNormal];
and to get the title (currentTitle is read-only and may be nil):
[b currentTitle];
BTW, if you just want to differentiate multiple buttons, you can just set the tag property (an integer value) of the buttons.
Also, check if you have the button specified as an IBOutlet in your viewController class, and is it connected properly as an outlet in Interface Builder?
I would rather set a certain Tag and compare the tag value rather than reading the title of the button since you have possibility to localize your app where button titles will possibly be different.
I was using the wrong property in Interface Builder.I was using name property of button in Interface Builder instead of the title property from the button settings.

perform:#selector using a method with parameters

I have a method hideButton
-(void) hideButton:(UIButton) *button {
[button setHidden:YES];
}
and I get a "can not use an object as parameter to a method" error.
I want to be able to give the button as a parameter to the method when calling this
[self performSelector:#selector(hideButton:smallestMonster1)
withObject:nil afterDelay:1.0];
How can this be done? as the above attempt doesnt work. I need to be able to give the button as a parameter or at least make the method aware of which button is calling to be hidden after 1 second.
Thanks
You can pass parameter to selector via withObject parameter:
[self performSelector:#selector(hideButton:) withObject:smallestMonster1 afterDelay:1.0];
Note that you can pass at most 1 parameter this way. If you need to pass more parameters you will need to use NSInvocation class for that.
Edit: Correct method declaration:
-(void) hideButton:(UIButton*) button
You must put parameter type inside (). Your hideButton method receives pointer to UIButton, so you should put UIButton* there

Selectors with arguments in Obj-C

So basically I have a big list of buttons that's present dropdowns and other things, and these buttons are created dynamically. So to capture the value for the appropriate button's data, I need to set it's action selector to a function that takes 1 extra parameter.
For example, using this selector on this dropdown, with the method below, returns an error that the selector is unrecognized. How can I get the selector to recognize the parameter I'm passing in? (In this case the variable 'name')
The apple docs at:
http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/ObjectiveC/Articles/ocSelectors.html#//apple_ref/doc/uid/TP30001163-CH23-SW1
On the last paragraph in the header 'The Target-Action Design Pattern', the Apple Docs imply that this can be done, but do not give an example of using a custom message, or maybe I'm just misunderstanding?
SEL sel = #selector(openDropdown:name:);
[dropdownSelector addTarget:self action:sel forControlEvents:UIControlEventTouchUpInside];
-(void) openDropdown: (NSString *) anotherArg : (id) sender {
// Stuff here based on anotherArg
}
You should be able to derive the clicked button's information from the id input arg
UIButton *button = (UIButton *) sender
NSString *title = [button currentTitle];
No need to pass the extra param
What you're asking can't be done. From the docs:
UIKit allows three different forms of action selector:
- (void)action
- (void)action:(id)sender
- (void)action:(id)sender forEvent:(UIEvent *)event
Since you have no influence on the event parameter, the sender object must include all information you want to pass to the action method.
Despite what you have written in your comment on mihirsm's answer, you can indeed subclass UIButton to add all the additional info you want to each button instance. You could also use the button's tag property to identify it (assign a unique tag to each button) and store all the additional info in an array or dictionary using the tags as keys.
Update: In the future, you can also use associative storage to add data to objects without subclassing them but this technology is not (yet) available on the iPhone platform (10.6 only at the moment).
CALayers support arbitrary keys for key-value coding; you can use this to attach arbitrary layers:
[[button1 layer] setValue:#"firstButtonData" forKey:#"myKey"];
[[button2 layer] setValue:#"secondButtonData" forKey:#"myKey"];
And later:
- (void)action:(id)sender forEvent:(UIEvent *)event
{
NSLog(#"Data for the button that was pressed: %#", [[sender layer] valueForKey:#"myKey"]);
}
Be careful not to collide with any of the existing properties on CALayer

How to add (id) sender to the following -(IBAction)?

How do you add a (id) sender to the following code?
- (IBAction) gobutton: (UIButton *) button5 {
Everything I try fails, any help would be appreciated. Thanks.
EDIT: I need to keep the (UIButton *) button 5 reference in the (IBAction)
If I recall correctly, and if you are using this in the way I think you are,
- (IBAction) gobutton: (id) sender {
if(sender == button5)
//do something...
else
//do something else...
}
Assuming that you specified button5 as a parameter to indicate that this executes in response to button5 being pressed.
Ok, first.... IBAction doesn't really mean anything special except to Interface Builder. Basically:
#define IBAction void
So whenever you see IBAction, think "void". The only reason it's there is as a flag to tell Interface Builder that a method is a valid method to connect control actions to. The Objective-C compiler doesn't need to know about it and so it's defined to void since all "action" methods return void.
Second, action methods also have one argument which could be an object of any number of types. Because of this, action methods are supposed to use type id as the type for their argument. That way they can be passed a pointer to any Objective-C object without causing the compiler to generate a type checking error.
So usually actions should work something like this:
- (IBAction)myAction:(id)sender {
if (sender == self.someButton) {
UIButton *button = (UIButton *)sender;
...
return;
} else if (sender == self.someControl) {
UIControl *control = (UIControl *)sender;
...
return;
}
}
In other words, an id is almost like an untyped pointer like a void * is routinely used in C when some function needs to take a pointer to something of unknown type. sender could be different types of control, so something generic like id is used then sender is cast to something more specific once the code knows what it is.
Anyway, there is absolutely no reason to define something as having a return type of IBAction unless you are going to use that method as a target action in Interface Builder. Having an IBAction in your app delegate seems kind of unusual....
It's not clear what you are trying to do but most actions look like:
- (IBAction) gobutton: (id)sender;
The first parameter to an action is always the sender (you can specify the type and name as appropriate).
If a method is the action for a button, then the first parameter will be the button. If that method is the action for several buttons, then the first parameter will allow you to determine which button was tapped (as Leper describes).
What problem are you actually trying to solve?
There are techniques for passing information to the action method. For example, if you have a button that appears on a table view cell and performs the same action for every cell, then in the action method, you would want to be able to determine which cell's button was tapped.
How can I get the id of the sender before the user touches the control?
Found it! Set a tag and the use viewWithTag.
Can you create a simple structure that contains both the UIButton and the sender and use that?
struct myObject
{
UIButton* button5;
id sender;
}
...or, you could create your own NSObject (probably more cocoa-y):
#instance myObject : NSObject
{
...
}