51

I want to pass the movie url from my dynamically generated button to MediaPlayer:

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

but action:@selector() withObject: does not work?

Is there any other solution?

Thanks for help!

Vaibhav Saran
  • 12,848
  • 3
  • 65
  • 75
Pascal Bayer
  • 2,615
  • 8
  • 33
  • 51

11 Answers11

63

Edit. Found a neater way!

One argument that the button can receive is (id)sender. This means you can create a new button, inheriting from UIButton, that allows you to store the other intended arguments. Hopefully these two snippets illustrate what to do.

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

and

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

This was my original suggestion.

There is a way to achieve the same result, but it's not pretty. Suppose you want to add a parameter to your navigate method. The code below will not allow you to pass that parameter to navigate.

[button addTarget:self action:@selector(navigate) forControlEvents:UIControlEventTouchUpInside];

To get around this you can move the navigate method to a class of it's own and set the "parameters" as attributes of that class...

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

Of course you can keep the navigate method in the original class and have it called by navAid, as long as it knows where to find it.

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

Like I said, it's not pretty, but it worked for me and I haven't found anyone suggesting any other working solution.

luk2302
  • 55,258
  • 23
  • 97
  • 137
WoodenKitty
  • 6,521
  • 8
  • 53
  • 73
  • 1
    the problem with your "original suggestion" is that your "NavigationAid" object is never released and is therefore a memory leak – user102008 Apr 22 '11 at 23:14
  • Can you post your NavigationAid Class? because method is not calling when i debug. – bhautikmewada191 Jul 12 '16 at 12:06
  • 2
    @bhautikmewada191 sorry but I don't have it. You're 5 years too late :P – WoodenKitty Jul 12 '16 at 23:37
  • May I know how do you declare your MyOwnButton class? I have declare it the following way but it is not working MyOwnButton.h ` #import @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 Nov 24 '20 at 03:59
32

I found solution. The call:

-(void) someMethod{
    UIButton * but;
    but.tag = 1;//some id button that you choice 
    [but addTarget:self action:@selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside]; 
}

And here the method called:

-(void) buttonPressed : (id) sender{
    UIButton *clicked = (UIButton *) sender;
    NSLog(@"%d",clicked.tag);//Here you know which button has pressed
}
orafaelreis
  • 321
  • 3
  • 2
  • Yes, UIButton does inherit the [`tag`](https://developer.apple.com/library/ios/documentation/uikit/reference/UIView_Class/UIView/UIView.html#//apple_ref/occ/instp/UIView/tag) property from `UIView`. That is handy. But the tag holds only a [`NSInteger`](https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Miscellaneous/Foundation_DataTypes/Reference/reference.html#//apple_ref/doc/c_ref/NSInteger). For other types, you can make a subclass as suggested by [Tristan's answer](http://stackoverflow.com/a/4930113/642706). – Basil Bourque Dec 19 '13 at 04:31
12

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

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
Trausti Thor
  • 3,722
  • 31
  • 41
11

You can sub-class a UIButton named MyButton, and pass the parameter by MyButton's properties.

Then, get the parameter back from (id)sender.

AechoLiu
  • 17,522
  • 9
  • 100
  • 118
  • yes this looks pretty good way to go bundle the things with instance of class and then get them back from it. But is't any other way to do passing the multiple parameters to @selector . – ashish Dec 22 '10 at 13:52
  • The sender will be MyButton, then you can get the parameter back from the properties of MyButton. – AechoLiu Dec 23 '10 at 01:05
3

Add the hidden titleLabel to the parameter is the best solution.

I generated a array arrUrl which store the NSURL of the mov files in my phone album by enumerate assets block.

After that, I grab on Frame, lets say, get the frame at 3:00 second in the movie file, and generated the image file from the frame.

Next, loop over the arrUrl, and use program generate the button with image in the button, append the button to subview of the self.view.

Because I have to pass the movie Url to playMovie function, I have to assign the button.titleLabel.text with one movie url. and the the button events function, retrieve the url from the buttontitleLable.txt.

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

You can set tag of the button and access it from sender in action

[btnHome addTarget:self action:@selector(btnMenuClicked:)     forControlEvents:UIControlEventTouchUpInside];
                    btnHome.userInteractionEnabled = YES;
                    btnHome.tag = 123;

In the called function

-(void)btnMenuClicked:(id)sender
{
[sender tag];

    if ([sender tag] == 123) {
        // Do Anything
    }
}
Fawad Masud
  • 12,219
  • 3
  • 25
  • 34
1

I think the correct method should be : - (void)addTarget:(id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents

UIControl Reference

Where do you get your method from?

I see that your selector has an argument, that argument will be filled by the runtime system. It will send you back the button through that argument.

Your method should look like:

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

vodkhang
  • 18,639
  • 11
  • 76
  • 110
  • Right, but I want to pass the movie url as parameter to my function buttonPressed. How can I do this? – Pascal Bayer Sep 15 '10 at 10:20
  • You have no way to do this. You should store your movie url somewhere like instance variable and then call it there – vodkhang Sep 15 '10 at 10:35
1

tl;dr: Use Blocks

For Obj-C, for example, there's a CocoaPod SHControlBlocks, whose usage would be:

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

For Swift, I love the pod Actions, which allows blocks for UIControls [1]:

// UIControl
let button = UIButton()
button.add(event: .touchUpInside) {
    print("Button tapped")
    playMusic(from: speakers_mp4, withSongAtPosition: indexPath.row)
}

Not that anyone is reading a 3-year old thread. ::crickets::

[1] And UIView, UITextField, UIGestureRecognizer, UIBarButtonItem, Timer (formally NSTimer), and NotificationCenter (formally NSNotificationCenter).

AmitaiB
  • 1,656
  • 1
  • 19
  • 19
0

To add to Tristan's answer, the button can also receive (id)event in addition to (id)sender:

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

This can be useful if, for example, the button is in a cell in a UITableView and you want to find the indexPath of the button that was touched (although I suppose this can also be found via the sender element).

Community
  • 1
  • 1
newenglander
  • 2,019
  • 24
  • 55
0

UIButton responds to addTarget:action:forControlEvents: since it inherits from UIControl. But it does not respond to addTarget:action:withObject:forControlEvents:

see reference for the method and for UIButton

You could extend UIButton with a category to implement that method, thought.

Daniel
  • 20,420
  • 10
  • 92
  • 149
  • and how would you implement this category? how would you pass parameters? you would still have the same problem, because anything you could do in a category method you could have done in the original calling code – user102008 Apr 22 '11 at 23:16
0

I have another solution in some cases.

store your parameter in a hidden UILabel. then add this UILabel as subview of UIButton.

when button is clicked, we can have a check on UIButton's all subviews. normally only 2 UILabel in it.

one is UIButton's title, the other is the one you just added. read that UILabel's text property, you will get the parameter.

This only apply for text parameter.

LetBulletFlies
  • 275
  • 2
  • 14