[ios] Passing parameters to addTarget:action:forControlEvents

I am using addTarget:action:forControlEvents like this:

[newsButton addTarget:self
action:@selector(switchToNewsDetails)
forControlEvents:UIControlEventTouchUpInside];

and I would like to pass parameters to my selector "switchToNewsDetails". The only thing I succeed in doing is to pass the (id)sender by writing:

action:@selector(switchToNewsDetails:)

But I am trying to pass variables like integer values. Writing it this way doesn't work :

int i = 0;
[newsButton addTarget:self
action:@selector(switchToNewsDetails:i)
forControlEvents:UIControlEventTouchUpInside];

Writing it this way does not work either:

int i = 0;
[newsButton addTarget:self
action:@selector(switchToNewsDetails:i:)
forControlEvents:UIControlEventTouchUpInside];

Any help would be appreciated :)

This question is related to ios objective-c iphone cocoa-touch uicontrolevents

The answer is


I was creating several buttons for each phone number in an array so each button needed a different phone number to call. I used the setTag function as I was creating several buttons within a for loop:

for (NSInteger i = 0; i < _phoneNumbers.count; i++) {

    UIButton *phoneButton = [[UIButton alloc] initWithFrame:someFrame];
    [phoneButton setTitle:_phoneNumbers[i] forState:UIControlStateNormal];

    [phoneButton setTag:i];

    [phoneButton addTarget:self
                    action:@selector(call:)
          forControlEvents:UIControlEventTouchUpInside];
}

Then in my call: method I used the same for loop and an if statement to pick the correct phone number:

- (void)call:(UIButton *)sender
{
    for (NSInteger i = 0; i < _phoneNumbers.count; i++) {
        if (sender.tag == i) {
            NSString *callString = [NSString stringWithFormat:@"telprompt://%@", _phoneNumbers[i]];
            [[UIApplication sharedApplication] openURL:[NSURL URLWithString:callString]];
        }
    }
}

To pass custom params along with the button click you just need to SUBCLASS UIButton.

(ASR is on, so there's no releases in the code.)

This is myButton.h

#import <UIKit/UIKit.h>

@interface myButton : UIButton {
    id userData;
}

@property (nonatomic, readwrite, retain) id userData;

@end

This is myButton.m

#import "myButton.h"
@implementation myButton
@synthesize userData;
@end

Usage:

myButton *bt = [myButton buttonWithType:UIButtonTypeCustom];
[bt setFrame:CGRectMake(0,0, 100, 100)];
[bt setExclusiveTouch:NO];
[bt setUserData:**(insert user data here)**];

[bt addTarget:self action:@selector(touchUpHandler:) forControlEvents:UIControlEventTouchUpInside];

[view addSubview:bt];

Recieving function:

- (void) touchUpHandler:(myButton *)sender {
    id userData = sender.userData;
}

If you need me to be more specific on any part of the above code — feel free to ask about it in comments.


This fixed my problem but it crashed unless I changed

action:@selector(switchToNewsDetails:event:)                 

to

action:@selector(switchToNewsDetails: forEvent:)              

Target-Action allows three different forms of action selector:

- (void)action
- (void)action:(id)sender
- (void)action:(id)sender forEvent:(UIEvent *)event

You can replace target-action with a closure (block in Objective-C) by adding a helper closure wrapper (ClosureSleeve) and adding it as an associated object to the control so it gets retained. That way you can pass any parameters.

Swift 3

class ClosureSleeve {
    let closure: () -> ()

    init(attachTo: AnyObject, closure: @escaping () -> ()) {
        self.closure = closure
        objc_setAssociatedObject(attachTo, "[\(arc4random())]", self, .OBJC_ASSOCIATION_RETAIN)
    }

    @objc func invoke() {
        closure()
    }
}

extension UIControl {
    func addAction(for controlEvents: UIControlEvents, action: @escaping () -> ()) {
        let sleeve = ClosureSleeve(attachTo: self, closure: action)
        addTarget(sleeve, action: #selector(ClosureSleeve.invoke), for: controlEvents)
    }
}

Usage:

button.addAction(for: .touchUpInside) {
    self.switchToNewsDetails(parameter: i)
}

See my comment above, and I believe you have to use NSInvocation when there is more than one parameter

more information on NSInvocation here

http://cocoawithlove.com/2008/03/construct-nsinvocation-for-any-message.html


I subclassed UIButton in CustomButton and I add a property where I store my data. So I call method: (CustomButton*) sender and in the method I only read my data sender.myproperty.

Example CustomButton:

@interface CustomButton : UIButton
@property(nonatomic, retain) NSString *textShare;
@end

Method action:

+ (void) share: (CustomButton*) sender
{
    NSString *text = sender.textShare;
    //your work…
}

Assign action

    CustomButton *btn = [[CustomButton alloc] initWithFrame: CGRectMake(margin, margin, 60, 60)];
    // other setup…

    btnWa.textShare = @"my text";
    [btn addTarget: self action: @selector(shareWhatsapp:)  forControlEvents: UIControlEventTouchUpInside];

If you just want to change the text for the leftBarButtonItem shown by the navigation controller together with the new view, you may change the title of the current view just before calling pushViewController to the wanted text and restore it in the viewHasDisappered callback for future showings of the current view.

This approach keeps the functionality (popViewController) and the appearance of the shown arrow intact.

It works for us at least with iOS 12, built with Xcode 10.1 ...


I made a solution based in part by the information above. I just set the titlelabel.text to the string I want to pass, and set the titlelabel.hidden = YES

Like this :

UIButton *imageclick = [[UIButton buttonWithType:UIButtonTypeCustom] retain];
imageclick.frame = photoframe;
imageclick.titleLabel.text = [NSString stringWithFormat:@"%@.%@", ti.mediaImage, ti.mediaExtension];
imageclick.titleLabel.hidden = YES;

This way, there is no need for a inheritance or category and there is no memory leak


There is another one way, in which you can get indexPath of the cell where your button was pressed:

using usual action selector like:

 UIButton *btn = ....;
    [btn addTarget:self action:@selector(yourFunction:) forControlEvents:UIControlEventTouchUpInside];

and then in in yourFunction:

   - (void) yourFunction:(id)sender {

    UIButton *button = sender;
    CGPoint center = button.center;
    CGPoint rootViewPoint = [button.superview convertPoint:center toView:self.tableView];
    NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:rootViewPoint];
    //the rest of your code goes here
    ..
}

since you get an indexPath it becames much simplier.


As there are many ways mentioned here for the solution, Except category feature .

Use the category feature to extend defined(built-in) element into your customisable element.

For instance(ex) :

@interface UIButton (myData)

@property (strong, nonatomic) id btnData;

@end

in the your view Controller.m

 #import "UIButton+myAppLists.h"

UIButton *myButton = // btn intialisation....
 [myButton set btnData:@"my own Data"];
[myButton addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];

Event handler:

-(void)buttonClicked : (UIButton*)sender{
    NSLog(@"my Data %@", sender. btnData);
}

Need more than just an (int) via .tag? Use KVC!

You can pass any data you want through the button object itself (by accessing CALayers keyValue dict).


Set your target like this (with the ":")

[myButton addTarget:self action:@selector(buttonTap:) forControlEvents:UIControlEventTouchUpInside];

Add your data(s) to the button itself (well the .layer of the button that is) like this:

NSString *dataIWantToPass = @"this is my data";//can be anything, doesn't have to be NSString
[myButton.layer setValue:dataIWantToPass forKey:@"anyKey"];//you can set as many of these as you'd like too!

Then when the button is tapped you can check it like this:

-(void)buttonTap:(UIButton*)sender{

    NSString *dataThatWasPassed = (NSString *)[sender.layer valueForKey:@"anyKey"];
    NSLog(@"My passed-thru data was: %@", dataThatWasPassed);

}

Examples related to ios

Adding a UISegmentedControl to UITableView Crop image to specified size and picture location Undefined Symbols error when integrating Apptentive iOS SDK via Cocoapods Keep placeholder text in UITextField on input in IOS Accessing AppDelegate from framework? Autoresize View When SubViews are Added Warp \ bend effect on a UIView? Speech input for visually impaired users without the need to tap the screen make UITableViewCell selectable only while editing Xcode 12, building for iOS Simulator, but linking in object file built for iOS, for architecture arm64

Examples related to objective-c

Adding a UISegmentedControl to UITableView Keep placeholder text in UITextField on input in IOS Accessing AppDelegate from framework? Warp \ bend effect on a UIView? Use NSInteger as array index Detect if the device is iPhone X Linker Command failed with exit code 1 (use -v to see invocation), Xcode 8, Swift 3 ITSAppUsesNonExemptEncryption export compliance while internal testing? How to enable back/left swipe gesture in UINavigationController after setting leftBarButtonItem? Change status bar text color to light in iOS 9 with Objective-C

Examples related to iphone

Detect if the device is iPhone X Xcode 8 shows error that provisioning profile doesn't include signing certificate Access files in /var/mobile/Containers/Data/Application without jailbreaking iPhone Certificate has either expired or has been revoked Missing Compliance in Status when I add built for internal testing in Test Flight.How to solve? cordova run with ios error .. Error code 65 for command: xcodebuild with args: "Could not find Developer Disk Image" Reason: no suitable image found iPad Multitasking support requires these orientations How to insert new cell into UITableView in Swift

Examples related to cocoa-touch

Include of non-modular header inside framework module Move textfield when keyboard appears swift Create space at the beginning of a UITextField Navigation bar with UIImage for title Generate a UUID on iOS from Swift How do I write a custom init for a UIView subclass in Swift? creating custom tableview cells in swift How would I create a UIAlertView in Swift? Get current NSDate in timestamp format How do you add an in-app purchase to an iOS application?

Examples related to uicontrolevents

Passing parameters to addTarget:action:forControlEvents