web-dev-qa-db-fra.com

Passage de paramètres à addTarget: action: forControlEvents

J'utilise addTarget: action: forControlEvents comme ceci:

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

et je voudrais passer des paramètres à mon sélecteur "switchToNewsDetails". La seule chose que j'ai réussi à faire est de transmettre l'expéditeur (id) en écrivant:

action:@selector(switchToNewsDetails:)

Mais j'essaie de transmettre des variables telles que des valeurs entières. L'écrire de cette façon ne fonctionne pas:

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

L'écrire de cette façon ne fonctionne pas non plus:

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

Toute aide serait appréciée :)

118
Pierre Espenan
action:@selector(switchToNewsDetails:)

Vous ne transmettez pas de paramètres à la méthode switchToNewsDetails: ici. Vous venez de créer un sélecteur pour faire en sorte que le bouton puisse l’appeler lorsque certaines actions se produisent (retouches dans votre cas). Les contrôles peuvent utiliser 3 types de sélecteurs pour répondre aux actions. Tous ont une signification prédéfinie pour leurs paramètres:

  1. sans paramètres

    action:@selector(switchToNewsDetails)
    
  2. avec 1 paramètre indiquant le contrôle qui envoie le message

    action:@selector(switchToNewsDetails:)
    
  3. Avec 2 paramètres indiquant le contrôle qui envoie le message et l'événement qui a déclenché le message: 

    action:@selector(switchToNewsDetails:event:)
    

Vous ne savez pas exactement ce que vous essayez de faire, mais si vous souhaitez attribuer un index de détails spécifique à chaque bouton, vous pouvez procéder comme suit:

  1. définir une propriété de balise sur chaque bouton égale à l'index requis
  2. dans la méthode switchToNewsDetails:, vous pouvez obtenir cet index et ouvrir les détails appropriés:

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

Pour transmettre des paramètres personnalisés avec le clic du bouton, il vous suffit de SUBCLASS UIButton.

(ASR est activé, donc il n'y a pas de version dans le code.)

C'est myButton.h

#import <UIKit/UIKit.h>

@interface myButton : UIButton {
    id userData;
}

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

@end

C'est 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];

Fonction de réception:

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

Si vous avez besoin de moi pour être plus spécifique sur une partie du code ci-dessus - n'hésitez pas à poser des questions dessus.

63
Yuriy Polezhayev

Target-Action permet de sélectionner trois formes d’action:

- (void)action
- (void)action:(id)sender
- (void)action:(id)sender forEvent:(UIEvent *)event
19
Benoît

Besoin de plus qu'un simple (int) via .tag? Utilisez KVC!

Vous pouvez transmettre les données de votre choix via l'objet bouton lui-même (en accédant à CALayers keyValue dict). 


Définissez votre cible comme ceci (avec le ":")

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

Ajoutez vos données au bouton lui-même (ainsi le .layer du bouton qui se trouve) comme ceci:

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!

Ensuite, lorsque vous appuyez sur le bouton, vous pouvez le vérifier comme suit:

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

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

}
12
Albert Renshaw

J'ai fait une solution basée en partie par les informations ci-dessus. Je viens de définir le titlelabel.text à la chaîne que je veux passer, et définir le titlelabel.hidden = YES

Comme ça :

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

De cette façon, il n'y a pas besoin d'un héritage ou d'une catégorie et il n'y a pas de fuite de mémoire

7
carelesslyChoosy

Je créais plusieurs boutons pour chaque numéro de téléphone dans un tableau, de sorte que chaque bouton devait appeler un numéro de téléphone différent. J'ai utilisé la fonction setTag car je créais plusieurs boutons dans une boucle for:

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

Ensuite, dans ma méthode call: j'ai utilisé la même chose pour loop et une instruction if pour choisir le numéro de téléphone correct:

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

Il existe un autre moyen d’obtenir indexPath de la cellule où le bouton a été enfoncé:

en utilisant le sélecteur d’action habituel comme:

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

puis dans votre fonction:

   - (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
    ..
}

puisque vous obtenez un indexPath, il est devenu beaucoup plus simple.

2
Anton Lisitsyn

Comme il existe de nombreuses façons mentionnées ici pour la solution, fonctionnalité catégorie sauf.

Utilisez la fonctionnalité category pour étendre un élément défini (intégré) dans votre élément personnalisable.

Par exemple (ex):

@interface UIButton (myData)

@property (strong, nonatomic) id btnData;

@end

dans votre vue Controller.m

 #import "UIButton+myAppLists.h"

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

Gestionnaire d'événements:

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

Cela a résolu mon problème mais il s'est écrasé à moins que je ne change 

action:@selector(switchToNewsDetails:event:)                 

à

action:@selector(switchToNewsDetails: forEvent:)              
1
iphaaw

Voir mon commentaire ci-dessus, et je crois que vous devez utiliser NSInvocation quand il y a plus d'un paramètre

plus d'informations sur NSInvocation ici

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

1
Aaron Saunders

Vous pouvez remplacer action-cible par une fermeture (bloc dans Objective-C) en ajoutant un wrapper de fermeture d'assistance (ClosureSleeve) et en l'ajoutant en tant qu'objet associé au contrôle pour le conserver. De cette façon, vous pouvez passer tous les paramètres.

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)
}
1
Marián Černý

Si vous souhaitez simplement modifier le texte du leftBarButtonItem indiqué par le contrôleur de navigation en même temps que la nouvelle vue, vous pouvez modifier le titre de la vue actuelle juste avant d'appeler pushViewController avec le texte souhaité et le restaurer dans le rappel viewHasDisappered pour les futures représentations de la vue en cours.

Cette approche conserve la fonctionnalité (popViewController) et l'apparence de la flèche affichée.

Cela fonctionne pour nous au moins avec iOS 12, construit avec Xcode 10.1 ...

0
Bubu

J'ai sous-classé UIButton dans CustomButton et j'ajoute une propriété dans laquelle je stocke mes données. J'appelle donc l'expéditeur de la méthode: (CustomButton *) et dans la méthode, je ne lis que mes données sender.myproperty.

Exemple de bouton personnalisé:

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

Action de la méthode:

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

Attribuer une 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];
0
iNiko84