views:

561

answers:

3

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
}
+1  A: 

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

Mihir Mathuria
I need more detail than the name of the button. The button has an assigned button type, id in core data, etc. I would extend UIButton and add the parameters I need, but because of the way UIButton works.. you can not actually add that type of thing by extending it. This also doesn't solve the real problem, which is that I just need to pass in more data than the sender. I would need to extend every class I use for every type of object in the app and add tons of redundant data if I wanted to do it this way.
Jameson
Without looking at your code, just off the top of my head -- can you possibly create a data structure, say an NSDictionary such that {key=buttonId, value=buttonData}. You can then access the buttonData from inside the Action method
Mihir Mathuria
I think that is what I may end up doing. All my data is actually in Core Data already so I can use a predicate to filter out the button with the specified title. The only problem is if there are two buttons with the same title, hopefully I can find a useful way to use the tag componenet to get the right values from core data.
Jameson
+2  A: 

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).

Ole Begemann
You can subclass UIButton but you can not access it's components. When creating a button you use the static function[MyCustomUIButton buttonWithType:UIButtonTypeRoundedRect]So when you access it later you don't get a MyCustomUIButton object, you get a derivative of the UIButtonTypeRoundedRect object. I know that this happens because I tried it, and then confirmed it by looking around on the web. It is possible to do but it involves actually subclassing a UIView and putting a button in it. It's all very convoluted and hack-ish. Do you have an alternative method?
Jameson
Using the tag component may be adequate, thanks for the heads up.
Jameson
A: 

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

rpetrich