AddTarget: action: forControlEvents için parametreler gönderme


125

AddTarget: action: forControlEvents şu şekilde kullanıyorum:

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

ve parametreleri "switchToNewsDetails" seçicime geçirmek istiyorum. Yapmayı başardığım tek şey (id) göndereni yazarak iletmektir:

action:@selector(switchToNewsDetails:)

Ama tamsayı değerleri gibi değişkenleri aktarmaya çalışıyorum. Bu şekilde yazmak işe yaramaz:

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

Bu şekilde yazmak da işe yaramıyor:

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

Herhangi bir yardım memnuniyetle karşılanacaktır :)


switchToNewsDetails için yöntem imzası nedir?
Aaron Saunders

- (void) switchToNewsDetails: (id) gönderen; - (void) switchToNewsDetails: (int) i: (id) gönderen;
Pierre Espenan

ama neye bağlıyım? her düğmeye özel mi? Cevabımı gör - ihtiyacınız olan şey etiket özelliği değil mi?
Vladimir


@PierreEspenan Şu anki kabul ettiğiniz cevabınız sadece tamsayıların etiket yoluyla geçmesine izin veriyor ki bu çok sınırlayıcı. HERHANGİ bir nesnenin geçmesine izin veren cevabıma değiştirmenizi tavsiye ederim . stackoverflow.com/a/40051706/2057171
Albert Renshaw

Yanıtlar:


173
action:@selector(switchToNewsDetails:)

Parametreleri switchToNewsDetails:burada metoda geçirmezsiniz. Sadece belirli bir eylem gerçekleştiğinde düğmeyi çağırabilmesi için bir seçici oluşturursunuz (sizin durumunuzda rötuş yapın). Kontroller, eylemlere yanıt vermek için 3 tür seçici kullanabilir, hepsinin parametrelerinin önceden tanımlanmış anlamı vardır:

  1. parametresiz

    action:@selector(switchToNewsDetails)
  2. mesajı gönderen kontrolü gösteren 1 parametre ile

    action:@selector(switchToNewsDetails:)
  3. Mesajı gönderen kontrolü ve mesajı tetikleyen olayı gösteren 2 parametre ile:

    action:@selector(switchToNewsDetails:event:)

Tam olarak ne yapmaya çalıştığınız belli değil, ancak her düğmeye belirli bir ayrıntı dizini atamak istediğinizi düşünerek aşağıdakileri yapabilirsiniz:

  1. her düğme için gerekli dizine eşit bir etiket özelliği ayarlayın
  2. içinde switchToNewsDetails:yöntemle o indeksini ve açık uygun Deatails edinebilirsiniz:

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

4
ya tamsayılardan başka bir şey aktarmaya çalışıyorsak? dizge gibi bir nesneyi iletmem gerekirse ne olur?
user102008

@user bağlamınız nedir? Ayrı ayrı geçmeniz gerekecek gibi görünüyor
Vladimir

çok teşekkürler. bu verileri nereden buldunuz? Bir dahaki sefere kendim bulabilmem için referansı her zaman takdir ederim.
bearMountain

1
: Bu bağlantıyı kontrol edebilirsiniz @bearMountain developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/... örneğin
Vladimir

3
Ayrı olarak geçirmenize gerek yok. Herhangi bir NSObject'i geçiyorsanız, sadece şunu söyleyebilirsiniz [button.layer setValue:yourObject forKey:@"anyKey"];ve ardından yöntemde şunu kontrol edin: (objectClass *)[button.layer valueForKey:@"anyKey"];Daha özgür bir sürümü gibi.tag
Albert Renshaw

64

Düğme tıklamasıyla birlikte özel parametreleri geçirmek için yalnızca SUBCLASS UIB düğmesine ihtiyacınız vardır .

(ASR açık, bu nedenle kodda sürüm yok.)

Bu myButton.h

#import <UIKit/UIKit.h>

@interface myButton : UIButton {
    id userData;
}

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

@end

Bu myButton.m

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

Kullanımı:

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

Alma işlevi:

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

Yukarıdaki kodun herhangi bir bölümünde daha spesifik olmama ihtiyacınız olursa - yorumlarda sormaktan çekinmeyin.


Sanırım en iyi yol bu
Çağatay Gürtürk

En zarif çözüm.
Victor C.

2
Harika cevap ... çok özelleştirilebilir. Tam olarak aradığım şey. Molodets! :)
denikov

geri bildirim için teşekkürler :) Birileri için yararlı olduğuna sevindim :)
Yuriy Polezhayev

1
Basit ve kullanışlı bir çözüm. Bunun tagAndroid görünümlerindeki mülke benzer olduğunu eklememe izin verin . Herhangi bir nesneyi saklayabilirler. Ayrıca , bir Sözlük / Harita'da olduğu gibi nesneleri anahtara göre de depolayabilirsiniz .
Ferran Maylinch

19

Hedef Eylem, üç farklı eylem seçiciye izin verir:

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

16

.Tag yoluyla (int) daha fazlasına mı ihtiyacınız var? KVC'yi kullanın!

İstediğiniz herhangi bir veriyi düğme nesnesinin kendisinden geçirebilirsiniz (CALayers keyValue dict'e erişerek).


Hedefinizi şu şekilde ayarlayın (":" ile)

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

Verilerinizi düğmenin kendisine ( .layeryani düğmenin) aşağıdaki gibi ekleyin :

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!

Ardından düğmeye dokunulduğunda, şu şekilde kontrol edebilirsiniz:

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

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

}

UIButton.layer'da bir nesneyi ayarlamak veya eklemek için bir yöntem yoktur. Bu çözümü nereden alıyorsun?
mAc

1
Bir UIButton, bir UIView türüdür, tüm UIViews CALayer .layer özelliğine sahiptir. CALayer, NSDictionary davranışlarını içerir. Unutmayın, bu Swift değil Objective-C, sorun bu olabilir mi? Apple'ın CALayer anahtar-değer kodlamasına ilişkin Belgeleri: developer.apple.com/library/content/documentation/Cocoa/…
Albert Renshaw

1
Üstteki cevap bu olmalı. Bunu yapmanın açık ara en kolay yolu !. Teşekkürler!
danielrosero

1
Cevap kabul edilmeli, çok daha kolay ve akıllıca bir yol.
Emon

1
Mükemmel cevap! Keşke bunu uzun zaman önce bilseydim.
John

7

Kısmen yukarıdaki bilgilere dayanarak bir çözüm yaptım. Titlelabel.text'i geçmek istediğim dizeye ayarladım ve titlelabel.hidden = YES'i ayarladım

Bunun gibi :

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

Böylelikle bir kalıtıma veya kategoriye gerek kalmaz ve bellek sızıntısı olmaz.


5

Bir dizideki her telefon numarası için birkaç düğme oluşturuyordum, bu nedenle her düğmenin aramak için farklı bir telefon numarasına ihtiyacı vardı. Bir for döngüsü içinde birkaç düğme oluştururken setTag işlevini kullandım:

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

Sonra çağrımda: yöntem aynı for döngüsünü ve doğru telefon numarasını seçmek için bir if ifadesini kullandım:

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

bu kodu geliştirebilirsiniz:- (void)call:(UIButton *)sender { NSString *phoneNumber = _phoneNumbers[i]; NSString *callString = [NSString stringWithFormat:@"telprompt://%@", phoneNumber]; [[UIApplication sharedApplication] openURL:[NSURL URLWithString:callString]]; }
Ladessa

2

Çözüm için burada belirtilen birçok yol olduğundan, Kategori hariç özelliği.

Tanımlanmış (yerleşik) öğeyi özelleştirilebilir öğenize genişletmek için kategori özelliğini kullanın.

Örneğin (ör.):

@interface UIButton (myData)

@property (strong, nonatomic) id btnData;

@end

Controller.m görünümünüzde

 #import "UIButton+myAppLists.h"

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

Olay işleyicisi:

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

Kategorilere gerçekten mülk ekleyemezsiniz.
HotFudgeSunday

2

Hedef eylemi bir kapanışla (Objective-C'de blok), bir yardımcı kapatma sarmalayıcısı (ClosureSleeve) ekleyerek ve onu denetime ilişkili bir nesne olarak ekleyerek, tutulmasıyla değiştirebilirsiniz. Bu şekilde herhangi bir parametreyi aktarabilirsiniz.

Hızlı 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)
    }
}

Kullanımı:

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

Yukarıdaki yöntem ( self.switchToNewsDetails(parameter: i)) 2 kez ateş ediyor, neden bana yardım et
Nagendar

Kod benim için çalışıyor. Pastebin.com/tPaqetMb yeni bir Tek Görünümlü Uygulama projesinde test edilmiştir . Yani sorun muhtemelen kodunuzdadır, button.addAction()iki kez aramak gibi . button.addActionKapanışın içindeki ilk satıra ve ayrıca bir kesme noktası ekleyin ve hata ayıklamayı kendiniz deneyin.
Marián Černý

2

Düğmenize bastığınız hücrenin indexPath'ini alabileceğiniz başka bir yol daha var:

olağan işlem seçiciyi kullanarak:

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

ve sonra İşlevinizde:

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

bir indexPath aldığınız için çok daha basit hale gelir.


1

Yukarıdaki yorumuma bakın ve birden fazla parametre olduğunda NSInvocation kullanmanız gerektiğine inanıyorum

NSInvocation hakkında daha fazla bilgi burada

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


Aslında, birkaç parametreyi geçmek istemiyorum, sadece bir tam sayı istiyorum.
Pierre Espenan

aslında performSelector: withObject: withObject: 2 parametreli seçicileri çağırmaya izin veren bir yöntem var. Ama daha fazlası için NSInvocation'a ihtiyacınız var
Vladimir

1

Bu sorunumu çözdü ama ben değiştirene kadar çöktü

action:@selector(switchToNewsDetails:event:)                 

için

action:@selector(switchToNewsDetails: forEvent:)              

0

CustomButton'da UIButton'ı alt sınıflara ayırdım ve verilerimi depoladığım bir özellik ekledim. Bu yüzden yöntemi çağırıyorum: (CustomButton *) gönderen ve yöntemde yalnızca veri gönderen.myproperty'yi okuyorum.

Örnek CustomButton:

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

Yöntem eylemi:

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

Eylem atayın

    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

Gezinme denetleyicisi tarafından gösterilen leftBarButtonItem metnini yeni görünümle birlikte değiştirmek istiyorsanız, pushViewController'ı istenen metne çağırmadan hemen önce geçerli görünümün başlığını değiştirebilir ve sonraki görünümler için mevcut görünüm.

Bu yaklaşım, işlevselliği (popViewController) ve gösterilen okun görünümünü olduğu gibi tutar.

Bizim için en azından Xcode 10.1 ile oluşturulmuş iOS 12 ile çalışıyor ...


Bu cevabın soruyla ilgili olduğunu sanmıyorum.
Pierre Espenan
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.