Przekazywanie parametrów do addTarget: action: forControlEvents

125

Używam addTarget: action: forControlEvents w ten sposób:

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

i chciałbym przekazać parametry do mojego selektora „switchToNewsDetails”. Jedyne, co mi się udało, to przekazać nadawcę (id) pisząc:

action:@selector(switchToNewsDetails:)

Ale próbuję przekazać zmienne, takie jak wartości całkowite. Pisanie w ten sposób nie działa:

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

Pisanie w ten sposób też nie działa:

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

Każda pomoc będzie mile widziana :)

Pierre Espenan
źródło
jaka jest sygnatura metody dla switchToNewsDetails?
Aaron Saunders
- (void) switchToNewsDetails: (id) sender; - (void) switchToNewsDetails: (int) i: (id) sender;
Pierre Espenan
ale na czym mi zależy? czy jest specyficzny dla każdego przycisku? Zobacz moją odpowiedź - czy właściwość tagu nie jest tym, czego potrzebujesz?
Vladimir
@PierreEspenan Twoja obecna zaakceptowana odpowiedź pozwala tylko na przekazywanie liczb całkowitych przez znacznik, co jest bardzo ograniczone. Zachęcam do zmiany tego na moją odpowiedź, która pozwala na przejście KAŻDEGO przedmiotu. stackoverflow.com/a/40051706/2057171
Albert Renshaw

Odpowiedzi:

173
action:@selector(switchToNewsDetails:)

Nie przekazujesz tutaj parametrów do switchToNewsDetails:metody. Po prostu tworzysz selektor, aby przycisk mógł go wywołać, gdy nastąpi określona akcja (w twoim przypadku retusz). Kontrolki mogą używać 3 typów selektorów do reagowania na akcje, wszystkie mają predefiniowane znaczenie swoich parametrów:

  1. bez parametrów

    action:@selector(switchToNewsDetails)
  2. z 1 parametrem wskazującym kontrolkę, która wysyła wiadomość

    action:@selector(switchToNewsDetails:)
  3. Z 2 parametrami wskazującymi kontrolkę, która wysyła wiadomość i zdarzenie, które wywołało wiadomość:

    action:@selector(switchToNewsDetails:event:)

Nie jest jasne, co dokładnie próbujesz zrobić, ale biorąc pod uwagę, że chcesz przypisać określony indeks szczegółów do każdego przycisku, możesz wykonać następujące czynności:

  1. ustaw właściwość tagu dla każdego przycisku równą wymaganemu indeksowi
  2. w switchToNewsDetails:metodzie możesz uzyskać ten indeks i otworzyć odpowiednie szczegóły:

    - (void)switchToNewsDetails:(UIButton*)sender{
        [self openDetails:sender.tag];
        // Or place opening logic right here
    }
Vladimir
źródło
4
co, jeśli próbujemy przekazać coś innego niż liczby całkowite? co jeśli muszę przekazać obiekt taki jak ciąg?
user102008
@użytkownik, jaki jest twój kontekst? Wygląda na to, że będziesz musiał przekazać to osobno
Vladimir,
wielkie dzięki. gdzie znalazłeś te dane? Zawsze doceniam odniesienie, więc może następnym razem mógłbym go znaleźć.
BearMountain
1
@bearMountain, możesz sprawdzić ten link: developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ ... na przykład
Vladimir
3
Nie musisz przekazywać go osobno. Jeśli przechodzisz jakikolwiek obiekt NSO, możesz po prostu powiedzieć, [button.layer setValue:yourObject forKey:@"anyKey"];a następnie w metodzie po prostu sprawdź (objectClass *)[button.layer valueForKey:@"anyKey"];To jest jak bardziej darmowa wersja.tag
Albert Renshaw
64

Aby przekazać własne parametry wraz z przyciskiem, wystarczy kliknąć przycisk SUBCLASS UIB .

(ASR jest włączony, więc w kodzie nie ma żadnych wydań).

To jest myButton.h

#import <UIKit/UIKit.h>

@interface myButton : UIButton {
    id userData;
}

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

@end

To jest myButton.m

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

Stosowanie:

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

Funkcja pobierania:

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

Jeśli chcesz, abym sprecyzował jakąkolwiek część powyższego kodu - możesz zapytać o to w komentarzach.

Yuriy Polezhayev
źródło
Myślę, że to najlepszy sposób
Çağatay Gürtürk
Najbardziej eleganckie rozwiązanie.
Victor C.
2
Świetna odpowiedź ... bardzo konfigurowalna. Dokładnie to, czego szukałem. MOLODETY! :)
denikov
dzięki za informację zwrotną :) Cieszę się, że komuś się przyda :)
Yuriy Polezhayev
1
Proste i przydatne obejście. Dodam tylko, że jest to podobne do tagwłaściwości w widokach Androida . Mogą przechowywać dowolny przedmiot. Możesz także przechowywać obiekty według klucza, jak w słowniku / mapie.
Ferran Maylinch
19

Target-Action umożliwia trzy różne formy selektora działań:

- (void)action
- (void)action:(id)sender
- (void)action:(id)sender forEvent:(UIEvent *)event
Benoît
źródło
16

Potrzebujesz czegoś więcej niż tylko (int) via .tag? Użyj KVC!

Możesz przekazać dowolne dane za pośrednictwem samego obiektu przycisku (uzyskując dostęp do polecenia CALayers keyValue dict).


Ustaw swój cel w ten sposób (za pomocą „:”)

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

Dodaj swoje dane do samego przycisku (dobrze .layerdo przycisku) w następujący sposób:

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!

Następnie po dotknięciu przycisku możesz to sprawdzić w ten sposób:

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

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

}
Albert Renshaw
źródło
w UIButton.layer nie ma metody ustawiania lub dodawania obiektu. Skąd masz to rozwiązanie?
mAc
1
UIButton to typ UIView, wszystkie UIViews mają właściwość CALayer .layer. CALayer zawiera zachowania NSDictionary. Zauważ, że to Objective-C, a nie Swift, czy może to być problem? Oto dokumentacja Apple dotycząca kodowania klucza i wartości CALayer: developer.apple.com/library/content/documentation/Cocoa/…
Albert Renshaw
1
To powinna być odpowiedź na górze. Zdecydowanie najłatwiej to zrobić !. dzięki!
danielrosero
1
Należy przyjąć odpowiedź, o wiele łatwiejszą i mądrzejszą.
Emon
1
Świetna odpowiedź! Chciałbym wiedzieć o tym dawno temu.
Jan
7

Rozwiązanie stworzyłem częściowo na podstawie powyższych informacji. Po prostu ustawiłem titlelabel.text na ciąg, który chcę przekazać, i ustawiłem titlelabel.hidden = TAK

Lubię to :

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

W ten sposób nie ma potrzeby dziedziczenia ani kategorii i nie ma wycieku pamięci

Abhinav Singh
źródło
5

Tworzyłem kilka przycisków dla każdego numeru telefonu w tablicy, więc każdy przycisk wymagał innego numeru telefonu do połączenia. Użyłem funkcji setTag, ponieważ tworzyłem kilka przycisków w pętli 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];
}

Następnie w mojej metodzie call: użyłem tej samej pętli for i instrukcji if, aby wybrać prawidłowy numer telefonu:

- (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
źródło
możesz ulepszyć ten kod:- (void)call:(UIButton *)sender { NSString *phoneNumber = _phoneNumbers[i]; NSString *callString = [NSString stringWithFormat:@"telprompt://%@", phoneNumber]; [[UIApplication sharedApplication] openURL:[NSURL URLWithString:callString]]; }
Ladessa
2

Ponieważ istnieje wiele sposobów rozwiązania, z wyjątkiem funkcji kategorii.

Użyj funkcji kategorii, aby rozszerzyć zdefiniowany (wbudowany) element na element dostosowywalny.

Na przykład (ex):

@interface UIButton (myData)

@property (strong, nonatomic) id btnData;

@end

w Twoim widoku Controller.m

 #import "UIButton+myAppLists.h"

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

Procedura obsługi zdarzeń:

-(void)buttonClicked : (UIButton*)sender{
    NSLog(@"my Data %@", sender. btnData);
}
Kumar KL
źródło
Tak naprawdę nie możesz dodawać właściwości w kategoriach.
HotFudge w niedzielę
2

Możesz zamienić akcję docelową na zamknięcie (blok w Objective-C), dodając otokę zamknięcia pomocnika (ClosureSleeve) i dodając ją jako skojarzony obiekt do kontrolki, aby została zachowana. W ten sposób możesz przekazać dowolne parametry.

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

Stosowanie:

button.addAction(for: .touchUpInside) {
    self.switchToNewsDetails(parameter: i)
}
Marián Černý
źródło
Powyżej metody ( self.switchToNewsDetails(parameter: i)) odpalanie 2 razy, dlaczego mi pomóż
Nagendar
Kod działa dla mnie. Przetestowano w nowym projekcie aplikacji Single View pastebin.com/tPaqetMb . Więc problem prawdopodobnie tkwi w twoim kodzie, jak button.addAction()dwukrotne wywołanie . Dodaj punkt przerwania do, button.addActiona także w pierwszym wierszu wewnątrz zamknięcia i spróbuj samodzielnie go debugować.
Marián Černý
2

Istnieje inny sposób, w jaki możesz uzyskać indexPath komórki, w której został naciśnięty przycisk:

używając zwykłego selektora akcji, takiego jak:

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

a następnie w 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
    ..
}

ponieważ otrzymujesz indexPath, staje się to znacznie prostsze.

Anton Lisitsyn
źródło
1

Zobacz mój komentarz powyżej i uważam, że musisz użyć NSInvocation, gdy jest więcej niż jeden parametr

więcej informacji na temat NSInvocation tutaj

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

Aaron Saunders
źródło
Właściwie nie chcę przekazywać kilku parametrów, chcę tylko liczbę całkowitą.
Pierre Espenan
w rzeczywistości istnieje metoda performSelector: withObject: withObject:, która umożliwia wywołanie selektorów z 2 parametrami. Ale po więcej potrzebujesz NSInvocation
Vladimir,
1

To rozwiązało mój problem, ale uległo awarii, chyba że się zmieniłem

action:@selector(switchToNewsDetails:event:)                 

do

action:@selector(switchToNewsDetails: forEvent:)              
iphaaw
źródło
0

Podklasowałem UIButton w CustomButton i dodaję właściwość, w której przechowuję moje dane. Wywołuję więc metodę: (CustomButton *) nadawca, aw metodzie tylko odczytam moje dane sender.myproperty.

Przykład CustomButton:

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

Działanie metody:

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

Przypisz działanie

    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
źródło
0

Jeśli chcesz tylko zmienić tekst dla leftBarButtonItem wyświetlanego przez kontroler nawigacji wraz z nowym widokiem, możesz zmienić tytuł bieżącego widoku tuż przed wywołaniem metody pushViewController na żądany tekst i przywrócić go w wywołaniu zwrotnym viewHasDisappered dla przyszłych pokazów aktualny widok.

Takie podejście zachowuje funkcjonalność (popViewController) i wygląd wyświetlanej strzałki w stanie nienaruszonym.

Działa dla nas przynajmniej z iOS 12, zbudowanym z Xcode 10.1 ...

Bubu
źródło
Nie sądzę, aby ta odpowiedź była związana z pytaniem.
Pierre Espenan