在按钮操作中传递参数:@selector

51

我想从动态生成的按钮中将电影网址传递给MediaPlayer:

[button addTarget:self action:@selector(buttonPressed:) withObject:[speakers_mp4 objectAtIndex:[indexPath row]] forControlEvents:UIControlEventTouchUpInside];

action:@selector() withObject:无效?

还有其他解决方案吗?

谢谢帮助!


嘿!有更好的解决方案。只是想确保你注意到了。 - WoodenKitty
11个回答

63

编辑。发现了一种更简洁的方法!

按钮可以接收的一个参数是(id)sender。这意味着你可以创建一个新的按钮,继承自UIButton,允许你存储其他预期的参数。希望这两个代码片段说明了如何操作。

  myOwnbutton.argOne = someValue
  [myOwnbutton addTarget:self action:@selector(buttonTouchUpInside:) forControlEvents:UIControlEventTouchUpInside];

- (IBAction) buttonTouchUpInside:(id)sender {
  MyOwnButton *buttonClicked = (MyOwnButton *)sender; 
  //do as you please with buttonClicked.argOne
}

这是我的原始建议。

有一种方法可以实现同样的结果,但它不太美观。假设您想向您的navigate方法添加一个参数。下面的代码将不允许您将该参数传递给navigate

[button addTarget:self action:@selector(navigate) forControlEvents:UIControlEventTouchUpInside];
为了解决这个问题,你可以将导航方法移到一个独立的类中,并将"参数"设置为该类的属性...
NavigationAid *navAid = [[NavigationAid alloc] init];
navAid.firstParam = someVariableOne
navAid.secondParam = someVariableTwo
[button addTarget:navAid action:@selector(navigate) forControlEvents:UIControlEventTouchUpInside];

当然,你可以将navigate方法保留在原始类中,并通过navAid调用它,只要navAid知道在哪里找到它。

NavigationAid *navAid = [[NavigationAid alloc] init];
navAid.whereToCallNavigate = self
navAid.firstParam = someVariableOne
navAid.secondParam = someVariableTwo
[button addTarget:navAid action:@selector(navigate) forControlEvents:UIControlEventTouchUpInside];

就像我之前所说的,这并不漂亮,但它对我起了作用,而且我没有找到其他人提出任何其他可行的解决方案。


1
你“原始建议”的问题在于你的“NavigationAid”对象从未被释放,因此存在内存泄漏。 - user102008
你能发布你的NavigationAid类吗?因为我在调试时发现方法没有被调用。 - bhautikmewada191
2
@bhautikmewada191 抱歉,我没有它。 你晚了5年 :P - WoodenKitty
请问您是如何声明您的MyOwnButton类的?我是按照以下方式声明的,但它不起作用。MyOwnButton.h` #import <UIKit/UIKit.h>@interface MyOwnButton : UIButton@property(nonatomic, strong) NSString* title; @property(nonatomic, strong) NSString* content;@end `MyOwnButton.m` #import "MyOwnButton.h"@implementation MyOwnButton @synthesize title; @synthesize content;@end ` - Richard Rodjues

32
我找到了解决方案。调用如下:
-(void) someMethod{
    UIButton * but;
    but.tag = 1;//some id button that you choice 
    [but addTarget:self action:@selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside]; 
}

并且这里调用了该方法:

-(void) buttonPressed : (id) sender{
    UIButton *clicked = (UIButton *) sender;
    NSLog(@"%d",clicked.tag);//Here you know which button has pressed
}

是的,UIButton确实从UIView继承了tag属性。这很方便。但是该标记仅保存NSInteger类型的值。对于其他类型,您可以像Tristan的答案中所建议的那样创建子类。 - Basil Bourque

12

我根据上面的信息做出了一部分解决方案。

我只是设置了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;

使用这种方法,不需要继承或类别,也不会出现内存泄漏问题。


1
谢谢,老兄。这个方法对我很有用。我正准备子类化UIButton时看到了这个。天哪! - Rakesh Singh
1
智能解决方案,感谢您的发布。 - Surbhi Garg

11

您可以通过继承一个名为MyButton的UIButton类,并在MyButton的属性中传递参数。

然后,从(id)sender中获取参数。


是的,这看起来是一个很好的方法,通过类的实例打包它们,然后从中获取它们。但是有没有其他方式可以将多个参数传递给@selector? - ashish
发送者将是MyButton,然后您可以从MyButton的属性中获取参数。 - AechoLiu

3
将隐藏的titleLabel添加到参数中是最佳解决方案。
我通过枚举assets block生成了一个存储手机相册mov文件NSURL的数组arrUrl。
接着,我抓取了一帧(例如在电影文件中的3:00秒),并从该帧生成图像文件。
接下来,循环遍历arrUrl,并使用程序生成带有图像按钮,将按钮附加到self.view的子视图中。
由于我必须传递电影URL以播放电影函数,因此我必须将按钮.titleLabel.text指定为一个电影URL。然后,在按钮事件函数中,从buttontitleLable.txt检索URL。
-(void)listVideos

{
   for(index=0;index<[self.arrUrl count];index++{
      UIButton *imageButton = [UIButton buttonWithType:UIButtonTypeCustom];
      imageButton.frame = CGRectMake(20,50+60*index,50,50);

      NSURL *dUrl = [self.arrUrl objectAtIndex:index];

      [imageButton setImage:[[UIImage allow] initWithCGImage:*[self getFrameFromeVideo:dUrl]] forState:UIControlStateNormal];
      [imageButton addTarget:self action:@selector(playMovie:) forControlEvents:UIControlEventTouchUpInside];
      imageButton.titleLabel.text = [NSString strinfWithFormat:@"%@",dUrl];
      imageButton.titleLabel.hidden = YES;

      [self.view addSubView:imageButton];
   }
}

-(void)playMovie:(id) sender{
   UIButton *btn = (UIButton *)sender;
   NSURL *movUrl = [NSURL URLWithString:btn.titleLabel.text];
   moviePlayer = [[MPMoviePlayerViewController alloc] initWithContentURL:movUrl];
   [self presentMoviePlayerViewControllerAnimated:moviePlayer];
}

-(CGIImageRef *)getFrameFromVideo:(NSURL *)mUrl{
   AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:mUrl option:nil];
   AVAssetImageGenerator *generator = [[AVAssetImageGenerator alloc] initWithAsset:asset];
   generator.appliesPreferredTrackTransform = YES;
   NSError *error =nil;
   CMTime = CMTimeMake(3,1);
   CGImageRef imageRef = [generator copyCGImageAtTime:time actualTime:nil error:&error];
   if(error !=nil) {
      NSLog(@"%@",sekf,error);
   }

   return @imageRef;
}

谢谢,这帮了我很多忙,但是不太聪明的是没有更简单的方法可以做到这一点。 - Kingsley Mitchell

1
你可以设置按钮的标签,并从发送者中访问它。
[btnHome addTarget:self action:@selector(btnMenuClicked:)     forControlEvents:UIControlEventTouchUpInside];
                    btnHome.userInteractionEnabled = YES;
                    btnHome.tag = 123;

在被调用的函数中。
-(void)btnMenuClicked:(id)sender
{
[sender tag];

    if ([sender tag] == 123) {
        // Do Anything
    }
}

1

我认为正确的方法应该是:

- (void)addTarget:(id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents

UIControl Reference

你从哪里得到这个方法的?

我看到你的选择器有一个参数,这个参数将由运行时系统填充。它将通过那个参数把按钮发送回给你。

你的方法应该像这样:

- (void)buttonPressed:(id)BUTTON_HERE { }


没错,但我想将电影URL作为参数传递给我的函数buttonPressed。我该怎么做? - Pascal Bayer
你没有办法这样做。你应该将电影的URL存储在某个地方,例如实例变量中,然后在那里调用它。 - vodkhang

1

简而言之:使用代码块

例如,对于Obj-C,有一个CocoaPod SHControlBlocks,其用法如下:

[self.btnFirst SH_addControlEvents:UIControlEventTouchDown withBlock:^(UIControl *sender) {
    [weakSelf performSegueWithIdentifier:@"second" sender:nil];
    NSLog(@"first");
  }];

对于Swift,我喜欢使用Actions这个pod,它可以为UIControl添加块[1]。
// UIControl
let button = UIButton()
button.add(event: .touchUpInside) {
    print("Button tapped")
    playMusic(from: speakers_mp4, withSongAtPosition: indexPath.row)
}

不是有人在看一个三年前的帖子。 ::蟋蟀::

[1] 和 UIViewUITextFieldUIGestureRecognizerUIBarButtonItemTimer(原名NSTimer)和 NotificationCenter(原名NSNotificationCenter)。


为什么是-1,伙计们?有什么需要改进/纠正的吗? - AmitaiB
谢谢你的支持! - AmitaiB

0

补充Tristan的回答,除了(id)sender,按钮还可以接收(id)event

- (IBAction) buttonTouchUpInside:(id)sender forEvent:(id)event { .... }

如果按钮位于UITableView的单元格中,并且您想要找到被触摸的按钮的indexPath,那么这将非常有用(尽管我认为这也可以通过sender元素找到)。


0

UIButton继承自UIControl,因此可以响应addTarget:action:forControlEvents:方法。但它不会响应addTarget:action:withObject:forControlEvents:方法。

请参考该方法的参考文献以及UIButton

你可以使用分类(category)来扩展UIButton并实现该方法。


你如何实现这个类别?你如何传递参数?你仍然会遇到同样的问题,因为你在类别方法中所能做的任何事情都可以在原始调用代码中完成。 - user102008

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接