Passando i parametri ad addTarget: action: forControlEvents


125

Sto usando addTarget: action: forControlEvents in questo modo:

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

e vorrei passare i parametri al mio selettore "switchToNewsDetails". L'unica cosa che riesco a fare è passare il mittente (id) scrivendo:

action:@selector(switchToNewsDetails:)

Ma sto cercando di passare variabili come valori interi. Scriverlo in questo modo non funziona:

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

Scriverlo in questo modo non funziona neanche:

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

Qualsiasi aiuto sarebbe apprezzato :)


qual è la firma del metodo per switchToNewsDetails?
Aaron Saunders,

- (void) switchToNewsDetails: mittente (id); - (void) switchToNewsDetails: (int) i: (id) mittente;
Pierre Espenan,

ma da cosa dipendo? è specifico per ciascun pulsante? Vedi la mia risposta: la proprietà del tag non è ciò di cui hai bisogno?
Vladimir,


@PierreEspenan La tua attuale risposta accettata consente solo il passaggio di numeri interi tramite tag, il che è molto limitante. Vi esorto a cambiarlo con la mia risposta che consenta il passaggio di QUALSIASI oggetto. stackoverflow.com/a/40051706/2057171
Albert Renshaw,

Risposte:


173
action:@selector(switchToNewsDetails:)

Non si passano i parametri al switchToNewsDetails:metodo qui. Devi solo creare un selettore per rendere il pulsante in grado di chiamarlo quando si verifica una determinata azione (ritocca nel tuo caso). I controlli possono utilizzare 3 tipi di selettori per rispondere alle azioni, tutti hanno un significato predefinito dei loro parametri:

  1. senza parametri

    action:@selector(switchToNewsDetails)
  2. con 1 parametro che indica il controllo che invia il messaggio

    action:@selector(switchToNewsDetails:)
  3. Con 2 parametri che indicano il controllo che invia il messaggio e l'evento che ha attivato il messaggio:

    action:@selector(switchToNewsDetails:event:)

Non è chiaro cosa si cerchi esattamente di fare, ma considerando che si desidera assegnare un indice di dettagli specifico a ciascun pulsante, è possibile effettuare le seguenti operazioni:

  1. impostare una proprietà tag per ciascun pulsante uguale all'indice richiesto
  2. nel switchToNewsDetails:metodo è possibile ottenere quell'indice e aprire gli appositi deatail:

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

4
cosa succede se stiamo provando a passare qualcosa di diverso dagli interi? cosa succede se devo passare un oggetto come una stringa?
user102008

@utente qual è il tuo contesto? Sembra che dovrai passarlo separatamente
Vladimir

molte grazie. dove hai trovato questi dati? Apprezzo sempre il riferimento in modo che forse potrei trovarlo da solo la prossima volta.
bearMountain

1
@bearMountain puoi controllare questo link: developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/… per esempio
Vladimir

3
Non è necessario passarlo separatamente. Se stai passando qualsiasi oggetto NSO puoi solo dire [button.layer setValue:yourObject forKey:@"anyKey"];e poi nel metodo basta controllare (objectClass *)[button.layer valueForKey:@"anyKey"];È come una versione più gratuita di.tag
Albert Renshaw,

64

Per passare parametri personalizzati insieme al pulsante fai clic su SUBCLASS UIButton .

(ASR è attivo, quindi non ci sono rilasci nel codice.)

Questo è myButton.h

#import <UIKit/UIKit.h>

@interface myButton : UIButton {
    id userData;
}

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

@end

Questo è myButton.m

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

Uso:

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

Funzione di ricezione:

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

Se hai bisogno che io sia più specifico su qualsiasi parte del codice di cui sopra, sentiti libero di chiederlo nei commenti.


Penso che questo sia il modo migliore
Çağatay Gürtürk,

La soluzione più elegante.
Victor C.,

2
Ottima risposta ... molto personalizzabile. Esattamente quello che stavo cercando. MOLODETS! :)
denikov il

grazie per il feedback :) Sono contento che sia utile per qualcuno :)
Yuriy Polezhayev

1
Soluzione semplice e utile. Vorrei solo aggiungere che questo è simile alla tagproprietà nelle viste Android . Possono memorizzare qualsiasi oggetto. E puoi anche memorizzare oggetti per chiave come in un dizionario / mappa.
Ferran Maylinch,

19

Target-Action consente tre diverse forme di selettore di azioni:

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

16

Hai bisogno di qualcosa di più di un semplice (int) tramite .tag? Usa KVC!

È possibile passare tutti i dati desiderati attraverso l'oggetto pulsante stesso (accedendo a CALayers keyValue dict).


Imposta il tuo obiettivo in questo modo (con ":")

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

Aggiungi i tuoi dati al pulsante stesso (e anche .layeral pulsante che è) in questo modo:

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!

Quindi quando si tocca il pulsante è possibile controllarlo in questo modo:

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

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

}

non esiste alcun metodo in UIButton.layer per impostare o aggiungere un oggetto. Dove prendi quella soluzione?
mAc,

1
Un UIButton è un tipo di UIView, tutte le UIVview hanno proprietà .layer CALayer. CALayer contiene comportamenti NSDictionary. Nota che questo è Objective-C non Swift, che potrebbe essere il problema? Ecco i documenti di Apple sulla codifica del valore-chiave di CALayer: developer.apple.com/library/content/documentation/Cocoa/…
Albert Renshaw

1
Questa dovrebbe essere la risposta in cima. Di gran lunga il modo più semplice per farlo !. Grazie!
danielrosero,

1
Dovrebbe essere accettata la risposta. Il suo modo molto più semplice e intelligente.
Emon,

1
Bella risposta! Vorrei averlo saputo molto tempo fa.
John,

7

Ho realizzato una soluzione basata in parte sulle informazioni di cui sopra. Ho appena impostato titlelabel.text sulla stringa che voglio passare e ho impostato titlelabel.hidden = YES

Come questo :

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

In questo modo, non è necessaria un'eredità o una categoria e non c'è perdita di memoria


5

Stavo creando diversi pulsanti per ciascun numero di telefono in un array, quindi ogni pulsante aveva bisogno di un numero di telefono diverso da chiamare. Ho usato la funzione setTag mentre stavo creando diversi pulsanti all'interno di un ciclo 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];
}

Quindi nella mia chiamata: metodo ho usato lo stesso per loop e un'istruzione if per scegliere il numero di telefono corretto:

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

puoi migliorare questo codice:- (void)call:(UIButton *)sender { NSString *phoneNumber = _phoneNumbers[i]; NSString *callString = [NSString stringWithFormat:@"telprompt://%@", phoneNumber]; [[UIApplication sharedApplication] openURL:[NSURL URLWithString:callString]]; }
Ladessa

2

Dato che ci sono molti modi qui menzionati per la soluzione, tranne la categoria categoria.

Utilizzare la funzione di categoria per estendere l'elemento definito (incorporato) nell'elemento personalizzabile.

Ad esempio (ex):

@interface UIButton (myData)

@property (strong, nonatomic) id btnData;

@end

nella tua vista Controller.m

 #import "UIButton+myAppLists.h"

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

Gestore di eventi:

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

Non puoi davvero aggiungere proprietà nelle categorie.
HotFudge domenica

2

È possibile sostituire l'azione bersaglio con una chiusura (blocco in Objective-C) aggiungendo un wrapper di chiusura helper (ClosureSleeve) e aggiungendolo come oggetto associato al controllo in modo che venga mantenuto. In questo modo puoi passare qualsiasi parametro.

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

Uso:

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

Sopra il metodo ( self.switchToNewsDetails(parameter: i)) sparando 2 volte, perché aiutarmi
Nagendar il

Il codice funziona per me. Testato in un nuovo progetto dell'app a visualizzazione singola pastebin.com/tPaqetMb . Quindi il problema è probabilmente nel tuo codice, come chiamare button.addAction()due volte. Aggiungi un punto di interruzione button.addActione anche sulla prima riga all'interno della chiusura e prova a eseguire il debug da solo.
Marián Černý,

2

C'è un altro modo, in cui è possibile ottenere indexPath della cella in cui è stato premuto il pulsante:

usando il solito selettore di azioni come:

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

e poi 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
    ..
}

dato che ottieni un indexPath diventa molto più semplice.


1

Vedi il mio commento sopra, e credo che devi usare NSInvocation quando c'è più di un parametro

maggiori informazioni su NSInvocation qui

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


In realtà, non voglio passare diversi parametri, voglio solo un numero intero.
Pierre Espenan,

attualmente c'è performSelector: withObject: withObject: metodo che consente di chiamare selettori con 2 parametri. Ma per di più hai bisogno di NSInvocation
Vladimir,

1

Ciò ha risolto il mio problema ma si è arrestato in modo anomalo a meno che non sia cambiato

action:@selector(switchToNewsDetails:event:)                 

per

action:@selector(switchToNewsDetails: forEvent:)              

0

Ho sottoclassato UIButton in CustomButton e ho aggiunto una proprietà in cui memorizzo i miei dati. Quindi chiamo metodo: (CustomButton *) mittente e nel metodo leggo solo i miei dati sender.myproperty.

Esempio CustomButton:

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

Metodo di azione:

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

Assegna azione

    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

Se si desidera solo modificare il testo per leftBarButtonItem mostrato dal controller di navigazione insieme alla nuova vista, è possibile modificare il titolo della vista corrente appena prima di chiamare pushViewController nel testo desiderato e ripristinarlo nella vista. la vista corrente.

Questo approccio mantiene intatte la funzionalità (popViewController) e l'aspetto della freccia mostrata.

Funziona per noi almeno con iOS 12, costruito con Xcode 10.1 ...


Non credo che questa risposta sia correlata alla domanda.
Pierre Espenan,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.