Übergeben von Parametern an addTarget: action: forControlEvents

125

Ich verwende addTarget: action: forControlEvents wie folgt:

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

und ich möchte Parameter an meinen Selektor "switchToNewsDetails" übergeben. Das einzige, was mir gelingt, ist, den (ID-) Absender schriftlich zu übergeben:

action:@selector(switchToNewsDetails:)

Aber ich versuche Variablen wie ganzzahlige Werte zu übergeben. So zu schreiben funktioniert nicht:

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

Es funktioniert auch nicht so:

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

Jede Hilfe wäre dankbar :)

Pierre Espenan
quelle
Was ist die Methodensignatur für switchToNewsDetails?
Aaron Saunders
- (void) switchToNewsDetails: (id) Absender; - (void) switchToNewsDetails: (int) i: (id) Absender;
Pierre Espenan
aber wovon bin ich abhängig? ist es spezifisch für jede Taste? Siehe meine Antwort - ist die Tag-Eigenschaft nicht das, was Sie brauchen?
Vladimir
@PierreEspenan Ihre aktuell akzeptierte Antwort erlaubt nur die Übergabe von Ganzzahlen über ein Tag, was sehr einschränkend ist. Ich fordere Sie auf, es in meine Antwort zu ändern, die das Weitergeben von JEDEM Objekt ermöglicht. stackoverflow.com/a/40051706/2057171
Albert Renshaw

Antworten:

173
action:@selector(switchToNewsDetails:)

Sie übergeben hier keine Parameter an die switchToNewsDetails:Methode. Sie erstellen einfach einen Selektor, damit die Schaltfläche ihn aufrufen kann, wenn eine bestimmte Aktion ausgeführt wird (in Ihrem Fall nachbessern). Steuerelemente können drei Arten von Selektoren verwenden, um auf Aktionen zu reagieren. Alle haben eine vordefinierte Bedeutung ihrer Parameter:

  1. ohne Parameter

    action:@selector(switchToNewsDetails)
  2. mit 1 Parameter, der die Steuerung angibt, die die Nachricht sendet

    action:@selector(switchToNewsDetails:)
  3. Mit 2 Parametern, die das Steuerelement angeben, das die Nachricht sendet, und das Ereignis, das die Nachricht ausgelöst hat:

    action:@selector(switchToNewsDetails:event:)

Es ist nicht klar, was genau Sie versuchen, aber wenn Sie jeder Schaltfläche einen bestimmten Detailindex zuweisen möchten, können Sie Folgendes tun:

  1. Legen Sie für jede Schaltfläche eine Tag-Eigenschaft fest, die dem erforderlichen Index entspricht
  2. In der switchToNewsDetails:Methode können Sie diesen Index erhalten und die entsprechenden Details öffnen:

    - (void)switchToNewsDetails:(UIButton*)sender{
        [self openDetails:sender.tag];
        // Or place opening logic right here
    }
Vladimir
quelle
4
Was ist, wenn wir versuchen, etwas anderes als ganze Zahlen zu übergeben? Was ist, wenn ich ein Objekt wie eine Zeichenfolge übergeben muss?
user102008
@user was ist dein Kontext? Scheint, Sie müssen es separat übergeben
Vladimir
Vielen Dank. Wo haben Sie diese Daten gefunden? Ich schätze die Referenz immer, damit ich sie beim nächsten Mal vielleicht selbst finden kann.
BearMountain
1
@bearMountain können Sie diesen Link überprüfen: developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/… zum Beispiel
Vladimir
3
Sie müssen es nicht separat übergeben. Wenn Sie ein NSObject übergeben, können Sie es einfach sagen [button.layer setValue:yourObject forKey:@"anyKey"];und dann in der Methode überprüfen (objectClass *)[button.layer valueForKey:@"anyKey"];, ob es sich um eine kostenlosere Version von.tag
Albert Renshaw vom
64

Um benutzerdefinierte Parameter zusammen mit dem Klick auf die Schaltfläche zu übergeben, müssen Sie nur die UIButton-Taste SUBCLASSEN .

(ASR ist aktiviert, daher enthält der Code keine Releases.)

Dies ist myButton.h

#import <UIKit/UIKit.h>

@interface myButton : UIButton {
    id userData;
}

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

@end

Dies ist myButton.m

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

Verwendung:

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

Empfangsfunktion:

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

Wenn Sie möchten, dass ich in einem Teil des obigen Codes genauer bin, können Sie dies gerne in den Kommentaren nachfragen.

Yuriy Polezhayev
quelle
Ich denke, das ist der beste Weg
Çağatay Gürtürk
Eleganteste Lösung.
Victor C.
2
Tolle Antwort ... sehr anpassbar. Genau das, wonach ich gesucht habe. MOLODETS! :)
denikov
danke für das Feedback :) Schön, dass es für jemanden nützlich ist :)
Yuriy Polezhayev
1
Einfache und nützliche Problemumgehung. Lassen Sie mich hinzufügen, dass dies der tagEigenschaft in Android-Ansichten ähnlich ist . Sie können jedes Objekt speichern. Sie können Objekte auch wie in einem Wörterbuch / einer Karte nach Schlüssel speichern .
Ferran Maylinch
19

Zielaktion ermöglicht drei verschiedene Formen der Aktionsauswahl:

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

Benötigen Sie mehr als nur ein (int) via .tag? Verwenden Sie KVC!

Sie können beliebige Daten über das Schaltflächenobjekt selbst übergeben (indem Sie auf CALayers keyValue dict zugreifen).


Stellen Sie Ihr Ziel so ein (mit dem ":")

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

Fügen Sie Ihre Daten der Schaltfläche selbst (und .layerder Schaltfläche) folgendermaßen hinzu:

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!

Wenn Sie dann auf die Schaltfläche tippen, können Sie dies folgendermaßen überprüfen:

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

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

}
Albert Renshaw
quelle
In UIButton.layer gibt es keine Methode zum Festlegen oder Hinzufügen eines Objekts. Woher bekommen Sie diese Lösung?
mAc
1
Ein UIButton ist eine Art von UIView. Alle UIViews haben die Eigenschaft CALayer .layer. CALayer enthält das Verhalten von NSDictionary. Beachten Sie, dass dies Objective-C und nicht Swift ist. Dies kann das Problem sein. Hier sind Apples Dokumente zur CALayer-Schlüsselwertcodierung: developer.apple.com/library/content/documentation/Cocoa/…
Albert Renshaw
1
Dies sollte die Antwort oben sein. Bei weitem der einfachste Weg, dies zu tun!. Vielen Dank!
Danielrosero
1
Es sollte Antwort akzeptiert werden. Es ist viel einfacher und klüger.
Emon
1
Gute Antwort! Ich wünschte, ich hätte das schon vor langer Zeit gewusst.
John
7

Ich habe eine Lösung gefunden, die teilweise auf den obigen Informationen basiert. Ich setze einfach den Titel titlelabel.text auf die Zeichenfolge, die ich übergeben möchte, und setze den Titel titlelabel.hidden = YES

So was :

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

Auf diese Weise ist keine Vererbung oder Kategorie erforderlich, und es liegt kein Speicherverlust vor

Abhinav Singh
quelle
5

Ich habe mehrere Tasten für jede Telefonnummer in einem Array erstellt, sodass jede Taste eine andere Telefonnummer zum Anrufen benötigte. Ich habe die setTag-Funktion verwendet, als ich mehrere Schaltflächen innerhalb einer for-Schleife erstellt habe:

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

Dann habe ich in meiner call: -Methode dieselbe for-Schleife und eine if-Anweisung verwendet, um die richtige Telefonnummer auszuwählen:

- (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]];
        }
    }
}
rjdunwoody91
quelle
Sie können diesen Code verbessern:- (void)call:(UIButton *)sender { NSString *phoneNumber = _phoneNumbers[i]; NSString *callString = [NSString stringWithFormat:@"telprompt://%@", phoneNumber]; [[UIApplication sharedApplication] openURL:[NSURL URLWithString:callString]]; }
Ladessa
2

Da es hier viele Möglichkeiten für die Lösung gibt, außer Kategoriefunktion.

Verwenden Sie die Kategoriefunktion, um das definierte (integrierte) Element in Ihr anpassbares Element zu erweitern.

Zum Beispiel (ex):

@interface UIButton (myData)

@property (strong, nonatomic) id btnData;

@end

in Ihrer Ansicht Controller.m

 #import "UIButton+myAppLists.h"

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

Ereignishandler:

-(void)buttonClicked : (UIButton*)sender{
    NSLog(@"my Data %@", sender. btnData);
}
Kumar KL
quelle
Sie können Eigenschaften in Kategorien nicht wirklich hinzufügen.
HotFudgeSunday
2

Sie können die Zielaktion durch einen Abschluss (Block in Objective-C) ersetzen, indem Sie einen Hilfsverschluss-Wrapper (ClosureSleeve) hinzufügen und ihn dem Steuerelement als zugehöriges Objekt hinzufügen, damit es beibehalten wird. Auf diese Weise können Sie beliebige Parameter übergeben.

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

Verwendung:

button.addAction(for: .touchUpInside) {
    self.switchToNewsDetails(parameter: i)
}
Marián Černý
quelle
Über Methode ( self.switchToNewsDetails(parameter: i)) 2 mal feuern, warum mir helfen
Nagendar
Der Code funktioniert bei mir. Getestet in einem neuen Single View App-Projekt pastebin.com/tPaqetMb . Das Problem liegt also wahrscheinlich in Ihrem Code, beispielsweise wenn Sie button.addAction()zweimal anrufen . Fügen Sie einen Haltepunkt zu button.addActionund auch in der ersten Zeile innerhalb des Verschlusses hinzu und versuchen Sie, ihn selbst zu debuggen.
Marián Černý
2

Es gibt eine andere Möglichkeit, wie Sie indexPath der Zelle abrufen können, in der Ihre Schaltfläche gedrückt wurde:

mit dem üblichen Aktionswähler wie:

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

und dann in Ihrer Funktion:

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

Da Sie einen Indexpfad erhalten, wird dieser viel einfacher.

Anton Lisitsyn
quelle
1

Siehe meinen Kommentar oben, und ich glaube, Sie müssen NSInvocation verwenden, wenn es mehr als einen Parameter gibt

Weitere Informationen zu NSInvocation finden Sie hier

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

Aaron Saunders
quelle
Eigentlich möchte ich nicht mehrere Parameter übergeben, sondern nur eine Ganzzahl.
Pierre Espenan
Tatsächlich gibt es die Methode performSelector: withObject: withObject:, mit der Selektoren mit zwei Parametern aufgerufen werden können. Aber für mehr brauchen Sie NSInvocation
Vladimir
1

Dies hat mein Problem behoben, ist aber abgestürzt, sofern ich mich nicht geändert habe

action:@selector(switchToNewsDetails:event:)                 

zu

action:@selector(switchToNewsDetails: forEvent:)              
iphaaw
quelle
0

Ich habe UIButton in CustomButton untergeordnet und eine Eigenschaft hinzugefügt, in der ich meine Daten speichere. Also rufe ich die Methode: (CustomButton *) sender auf und lese in der Methode nur meine Daten sender.myproperty.

Beispiel CustomButton:

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

Methodenaktion:

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

Aktion zuweisen

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

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

Wenn Sie nur den Text für das vom Navigationscontroller angezeigte leftBarButtonItem zusammen mit der neuen Ansicht ändern möchten, können Sie den Titel der aktuellen Ansicht ändern, bevor Sie pushViewController in den gewünschten Text aufrufen, und ihn im viewHasDisappered-Rückruf für zukünftige Anzeigen von wiederherstellen die aktuelle Ansicht.

Dieser Ansatz behält die Funktionalität (popViewController) und das Erscheinungsbild des angezeigten Pfeils bei.

Es funktioniert für uns zumindest mit iOS 12, das mit Xcode 10.1 erstellt wurde ...

Bubu
quelle
Ich glaube nicht, dass diese Antwort mit der Frage zusammenhängt.
Pierre Espenan