将参数传递给addTarget:action:forControlEvents


125

我正在使用addTarget:action:forControlEvents这样的:

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

并且我想将参数传递给选择器“ switchToNewsDetails”。我唯一成功完成的事情是通过编写(id)sender来传递:

action:@selector(switchToNewsDetails:)

但是我试图传递像整数值这样的变量。用这种方式写不起作用:

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

用这种方式编写也不起作用:

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

任何帮助,将不胜感激 :)


switchToNewsDetails的方法签名是什么?
亚伦·桑德斯

-(void)switchToNewsDetails:(id)sender; -(void)switchToNewsDetails:(int)i:(id)sender;
皮埃尔·埃斯佩南

但是我依赖什么?每个按钮具体吗?看看我的答案-标签属性不是您所需要的吗?
弗拉基米尔


@PierreEspenan您当前接受的答案仅允许通过标记传递整数,这是非常有限的。我敦促您将其更改为允许任何对象传递的答案。stackoverflow.com/a/40051706/2057171
艾伯特·伦肖2016年

Answers:


173
action:@selector(switchToNewsDetails:)

您没有switchToNewsDetails:在此处将参数传递给方法。您只需创建一个选择器,以使按钮能够在发生某些操作(根据情况进行修饰)时调用它。控件可以使用3种选择器来响应动作,所有选择器均具有其参数的预定义含义:

  1. 没有参数

    action:@selector(switchToNewsDetails)
  2. 具有1个参数,指示发送消息的控件

    action:@selector(switchToNewsDetails:)
  3. 使用2个参数指示发送消息的控件和触发消息的事件:

    action:@selector(switchToNewsDetails:event:)

目前尚不清楚您到底打算做什么,但是考虑到要为每个按钮分配一个特定的详细信息索引,可以执行以下操作:

  1. 为每个按钮设置一个标记属性,使其等于所需的索引
  2. switchToNewsDetails:方法中,您可以获得该索引并打开适当的详细信息:

    - (void)switchToNewsDetails:(UIButton*)sender{
        [self openDetails:sender.tag];
        // Or place opening logic right here
    }

4
如果我们尝试传递整数以外的值怎么办?如果我需要传递类似字符串的对象怎么办?
user102008 2011年

@user您的上下文是什么?似乎您需要单独通过
弗拉基米尔(Vladimir)

非常感谢。您在哪里找到这些数据?我总是很感激参考资料,以便下次可以自己找到它。
bearMountain 2012年

1
@bearMountain,您可以查看以下链接:例如
弗拉基米尔(Vladimir)

3
您不需要单独传递它。如果要传递任何NSObject,则可以说[button.layer setValue:yourObject forKey:@"anyKey"];,然后在方法中选中“ (objectClass *)[button.layer valueForKey:@"anyKey"];它就像.tag
Albert Renshaw

64

要与按钮一起传递自定义参数,您只需要SUBCLASS UIButton即可

(ASR已启用,因此代码中没有发布。)

这是myButton.h

#import <UIKit/UIKit.h>

@interface myButton : UIButton {
    id userData;
}

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

@end

这是myButton.m

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

用法:

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];

接收功能:

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

如果您需要我在上述代码的任何部分上更加具体,请随时在注释中询问。


我觉得这是最好的办法
ÇağatayGürtürk

最优雅的解决方案。
胜者C.

2
很好的答案...非常可定制。正是我想要的。恶魔!:)
denikov 2014年

感谢您的反馈:)很高兴,它对某人有用:)
Yuriy Polezhayev 2014年

1
简单有用的解决方法。让我补充一点,这类似于tagAndroid视图中属性。它们可以存储任何对象。而且您还可以像在字典/地图中一样通过键存储对象
Ferran Maylinch 2015年

19

Target-Action允许三种不同形式的动作选择器:

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

16

是否需要通过.tag不仅仅是一个(int)?使用KVC!

您可以通过按钮对象本身传递任何所需的数据(通过访问CALayers keyValue dict)。


像这样设置目标(使用“:”)

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

将您的数据添加到按钮本身(以及.layer按钮的内容)中,如下所示:

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!

然后,当点击按钮时,您可以像这样检查它:

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

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

}

UIButton.layer中没有设置或添加对象的方法。您从哪里获得解决方案?
mAc

1
UIButton是一种UIView,所有UIView都具有CALayer .layer属性。CALayer包含NSDictionary行为。请注意,这是Objective-C而不是Swift,这可能是问题所在吗?下面是苹果的CALayer的键值文件编码:developer.apple.com/library/content/documentation/Cocoa/...
阿尔伯特·伦肖

1
这应该是最重要的答案。到目前为止,最简单的方法!谢谢!
danielrosero

1
应该接受的答案。它更简单,更智能。
Emon

1
好答案!希望我早就知道这一点。
约翰,

7

我部分基于上述信息提出了解决方案。我只是将titlelabel.text设置为我要传递的字符串,然后将titlelabel.hidden = YES设置为

像这样 :

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

这样,就不需要继承或类别,也没有内存泄漏


5

我正在为数组中的每个电话号码创建多个按钮,因此每个按钮都需要一个不同的电话号码才能进行呼叫。我在set循环中创建多个按钮时使用了setTag函数:

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];
}

然后在我的call:方法中,我使用了相同的for循环和if语句来选择正确的电话号码:

- (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]];
        }
    }
}

您可以改善此代码:- (void)call:(UIButton *)sender { NSString *phoneNumber = _phoneNumbers[i]; NSString *callString = [NSString stringWithFormat:@"telprompt://%@", phoneNumber]; [[UIApplication sharedApplication] openURL:[NSURL URLWithString:callString]]; }
Ladessa

2

由于这里提到了许多解决方案的方法,所以除了类别功能。

使用类别功能可将define(内置)元素扩展到您的可自定义元素中。

例如(ex):

@interface UIButton (myData)

@property (strong, nonatomic) id btnData;

@end

在您的视图Controller.m中

 #import "UIButton+myAppLists.h"

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

事件处理程序:

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

您不能真正在类别中添加属性。
HotFudgeSunday

2

您可以通过添加辅助闭合包装器(ClosureSleeve)并将其作为关联对象添加到控件中,从而保留它,从而用闭合(在Objective-C中的块)替换目标动作。这样,您可以传递任何参数。

迅捷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)
    }
}

用法:

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

上面的方法(self.switchToNewsDetails(parameter: i))发射了2次,为什么要帮助我
Nagendar

该代码对我有用。在新的Single View App项目pastebin.com/tPaqetMb中进行了测试。因此,问题可能出在您的代码中,例如调用button.addAction()两次。添加一个断点到button.addAction闭包内的第一行上,然后尝试自己进行调试。
玛丽安·塞尔尼

2

还有另一种方法,您可以获取按下按钮的单元格的indexPath:

使用通常的动作选择器,例如:

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

然后在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
    ..
}

由于您获得了indexPath,因此变得更加简单。


1

请参阅上面的评论,我相信当有多个参数时,您必须使用NSInvocation

有关NSInvocation的更多信息,请点击此处

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


其实,我不想传递几个参数,我只想要一个整数。
皮埃尔·埃斯佩南

实际上,有performSelector:withObject:withObject:方法,该方法允许使用2个参数调用选择器。但更多则需要NSInvocation
Vladimir

1

这解决了我的问题,但是除非我更改,否则它就会崩溃

action:@selector(switchToNewsDetails:event:)                 

action:@selector(switchToNewsDetails: forEvent:)              

0

我在CustomButton中将UIButton子类化,并在存储数据的位置添加了一个属性。因此,我调用方法:(CustomButton *)发送方,在该方法中,我仅读取我的数据sender.myproperty。

示例CustomButton:

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

方法动作:

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

分配动作

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

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

0

如果您只想更改导航控制器显示的leftBarButtonItem的文本以及新视图,则可以在将pushViewController调用为所需文本之前更改当前视图的标题,并将其恢复到viewHasDisappered回调中以供将来查看当前视图。

这种方法使功能(popViewController)和所示箭头的外观保持完整。

它至少在iOS 12上对我们有用,而iOS 12是用Xcode 10.1构建的。


我认为此答复与问题无关。
皮埃尔·埃斯佩南
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.