Melewati parameter ke addTarget: action: forControlEvents

125

Saya menggunakan addTarget: action: forControlEvents seperti ini:

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

dan saya ingin meneruskan parameter ke pemilih saya "switchToNewsDetails". Satu-satunya hal yang berhasil saya lakukan adalah mengirimkan pengirim (id) dengan menulis:

action:@selector(switchToNewsDetails:)

Tetapi saya mencoba untuk mengirimkan variabel seperti nilai integer. Menulisnya dengan cara ini tidak berhasil:

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

Menulisnya dengan cara ini juga tidak berfungsi:

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

Bantuan apa pun akan dihargai :)

Pierre Espenan
sumber
apa metode tanda tangan untuk switchToNewsDetails?
Aaron Saunders
- (void) switchToNewsDetails: pengirim (id); - (void) switchToNewsDetails: (int) i: (id) pengirim;
Pierre Espenan
tapi apa yang saya andalkan? apakah itu spesifik untuk setiap tombol? Lihat jawaban saya - bukankah properti tag adalah yang Anda butuhkan?
Vladimir
@PierreEspenan Jawaban Anda saat ini yang diterima hanya memungkinkan lewat bilangan bulat melalui tag yang sangat membatasi. Saya mendorong Anda untuk mengubahnya ke jawaban saya yang memungkinkan lewatnya objek APA PUN . stackoverflow.com/a/40051706/2057171
Albert Renshaw

Jawaban:

173
action:@selector(switchToNewsDetails:)

Anda tidak meneruskan parameter ke switchToNewsDetails:metode di sini. Anda cukup membuat pemilih untuk membuat tombol dapat memanggilnya ketika tindakan tertentu terjadi (sentuh dalam kasing Anda). Kontrol dapat menggunakan 3 jenis penyeleksi untuk merespons tindakan, semuanya memiliki makna parameter yang telah ditentukan sebelumnya:

  1. tanpa parameter

    action:@selector(switchToNewsDetails)
  2. dengan 1 parameter yang menunjukkan kontrol yang mengirim pesan

    action:@selector(switchToNewsDetails:)
  3. Dengan 2 parameter yang menunjukkan kontrol yang mengirim pesan dan peristiwa yang memicu pesan:

    action:@selector(switchToNewsDetails:event:)

Tidak jelas apa sebenarnya yang Anda coba lakukan, tetapi mengingat Anda ingin menetapkan indeks detail spesifik untuk setiap tombol, Anda dapat melakukan hal berikut:

  1. atur properti tag ke setiap tombol sama dengan indeks yang diperlukan
  2. dalam switchToNewsDetails:metode Anda dapat memperoleh indeks itu dan membuka deatails yang sesuai:

    - (void)switchToNewsDetails:(UIButton*)sender{
        [self openDetails:sender.tag];
        // Or place opening logic right here
    }
Vladimir
sumber
4
bagaimana jika kita mencoba melewatkan sesuatu selain bilangan bulat? bagaimana jika saya harus melewatkan objek seperti string?
user102008
@ pengguna apa konteks Anda? Sepertinya Anda harus melewatinya secara terpisah
Vladimir
Terima kasih banyak. di mana Anda menemukan data ini? Saya selalu menghargai referensi sehingga mungkin saya bisa menemukannya sendiri di lain waktu.
bearMountain
1
@bearMountain Anda dapat memeriksa tautan ini: developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/… misalnya
Vladimir
3
Anda tidak perlu meneruskannya secara terpisah. Jika Anda melewati sembarang NSObject, Anda dapat mengatakannya [button.layer setValue:yourObject forKey:@"anyKey"];dan kemudian dalam metode ini, periksa saja (objectClass *)[button.layer valueForKey:@"anyKey"];Ini seperti versi yang lebih bebas dari.tag
Albert Renshaw
64

Untuk melewati params khusus bersama dengan tombol klik Anda hanya perlu SUBCLASS UIButton .

(ASR aktif, jadi tidak ada rilis dalam kode.)

Ini myButton.h

#import <UIKit/UIKit.h>

@interface myButton : UIButton {
    id userData;
}

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

@end

Ini myButton.m

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

Pemakaian:

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

Fungsi penerima:

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

Jika Anda membutuhkan saya untuk lebih spesifik pada bagian mana pun dari kode di atas - jangan ragu untuk menanyakannya dalam komentar.

Yuriy Polezhayev
sumber
Saya pikir ini adalah cara terbaik
Çağatay Gürtürk
Solusi paling elegan.
Victor C.
2
Jawaban bagus ... sangat bisa disesuaikan. Persis apa yang saya cari. MOLODET! :)
denikov
terima kasih atas umpan baliknya :) Senang, itu berguna untuk seseorang :)
Yuriy Polezhayev
1
Solusi sederhana dan bermanfaat. Izinkan saya menambahkan bahwa ini mirip dengan tagproperti dalam tampilan Android . Mereka dapat menyimpan objek apa pun. Dan Anda juga dapat menyimpan objek dengan kunci seperti di Kamus / Peta.
Ferran Maylinch
19

Target-Action memungkinkan tiga bentuk pemilih tindakan yang berbeda:

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

Perlu lebih dari sekadar (int) via .tag? Gunakan KVC!

Anda dapat meneruskan data apa pun yang Anda inginkan melalui objek tombol itu sendiri (dengan mengakses CALayers keyValue dict).


Tetapkan target Anda seperti ini (dengan ":")

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

Tambahkan data Anda ke tombol itu sendiri (baik .layerdari tombol yang ada) seperti ini:

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!

Kemudian ketika tombol diketuk Anda dapat memeriksanya seperti ini:

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

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

}
Albert Renshaw
sumber
tidak ada metode di UIButton.layer untuk mengatur atau menambahkan objek. Di mana Anda mendapatkan solusi itu?
mAc
1
UIButton adalah jenis UIView, semua UIViews memiliki properti CALayer .layer. CALayer berisi perilaku NSDictionary. Catatan ini Objective-C bukan Swift, itu mungkin masalahnya? Berikut ini adalah Dokumen milik Apple tentang pengkodean nilai kunci CALayer: developer.apple.com/library/content/documentation/Cocoa/…
Albert Renshaw
1
Ini harus menjadi jawaban di atas. Sejauh ini cara termudah untuk melakukannya !. Terima kasih!
danielrosero
1
Itu harus diterima jawaban. Jauh lebih mudah dan cara cerdas.
Emon
1
Jawaban bagus! Seandainya saya tahu ini sudah lama sekali.
John
7

Saya membuat solusi berdasarkan informasi di atas. Saya hanya mengatur titlelabel.text ke string yang ingin saya sampaikan, dan mengatur titlelabel.hidden = YA

Seperti ini :

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

Dengan cara ini, tidak perlu warisan atau kategori dan tidak ada kebocoran memori

Abhinav Singh
sumber
5

Saya membuat beberapa tombol untuk setiap nomor telepon dalam array sehingga setiap tombol memerlukan nomor telepon yang berbeda untuk menelepon. Saya menggunakan fungsi setTag karena saya membuat beberapa tombol dalam for loop:

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

Kemudian dalam panggilan saya: metode yang saya gunakan sama untuk loop dan pernyataan if untuk memilih nomor telepon yang benar:

- (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
sumber
Anda dapat meningkatkan kode ini:- (void)call:(UIButton *)sender { NSString *phoneNumber = _phoneNumbers[i]; NSString *callString = [NSString stringWithFormat:@"telprompt://%@", phoneNumber]; [[UIApplication sharedApplication] openURL:[NSURL URLWithString:callString]]; }
Ladessa
2

Karena ada banyak cara yang disebutkan di sini untuk solusinya, Kecuali fitur kategori.

Gunakan fitur kategori untuk memperluas elemen yang sudah ada (built-in) ke elemen Anda yang dapat disesuaikan.

Misalnya (mis):

@interface UIButton (myData)

@property (strong, nonatomic) id btnData;

@end

di Controller tampilan Anda

 #import "UIButton+myAppLists.h"

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

Penangan acara:

-(void)buttonClicked : (UIButton*)sender{
    NSLog(@"my Data %@", sender. btnData);
}
Kumar KL
sumber
Anda tidak dapat menambahkan properti dalam kategori.
HotFudgeSunday
2

Anda dapat mengganti aksi target dengan penutup (blok dalam Objective-C) dengan menambahkan pembungkus penutupan pembantu (ClosureSleeve) dan menambahkannya sebagai objek terkait ke kontrol sehingga tetap dipertahankan. Dengan begitu Anda dapat melewati parameter apa pun.

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

Pemakaian:

button.addAction(for: .touchUpInside) {
    self.switchToNewsDetails(parameter: i)
}
Marián Černý
sumber
Metode di atas ( self.switchToNewsDetails(parameter: i)) menembakkan 2 kali, mengapa membantu saya
Nagendar
Kode ini berfungsi untuk saya. Diuji dalam proyek Aplikasi Tampilan Tunggal baru pastebin.com/tPaqetMb . Jadi masalahnya mungkin ada di kode Anda, seperti menelepon button.addAction()dua kali. Tambahkan breakpoint ke button.addActiondan juga pada baris pertama di dalam penutupan dan coba debug sendiri.
Marián Černý
2

Ada satu cara lain, di mana Anda bisa mendapatkan indexPath sel tempat tombol Anda ditekan:

menggunakan pemilih tindakan biasa seperti:

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

dan kemudian di dalam Fungsi Anda:

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

karena Anda mendapatkan indexPath, itu menjadi jauh lebih simpel.

Anton Lisitsyn
sumber
1

Lihat komentar saya di atas, dan saya yakin Anda harus menggunakan NSInvocation ketika ada lebih dari satu parameter

informasi lebih lanjut tentang NSInvocation di sini

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

Aaron Saunders
sumber
Sebenarnya, saya tidak ingin melewati beberapa params, saya hanya ingin integer.
Pierre Espenan
sebenarnya ada performSelector: withObject: withObject: metode yang memungkinkan untuk memanggil penyeleksi dengan 2 parameter. Tetapi untuk lebih lanjut Anda membutuhkan NSInvocation
Vladimir
1

Ini memperbaiki masalah saya tetapi macet kecuali saya berubah

action:@selector(switchToNewsDetails:event:)                 

untuk

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

Saya subkelas UIButton di CustomButton dan saya menambahkan properti tempat saya menyimpan data saya. Jadi saya memanggil metode: (CustomButton *) pengirim dan dalam metode saya hanya membaca data sender.myproperty saya.

Contoh CustomButton:

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

Metode tindakan:

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

Tetapkan tindakan

    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
sumber
0

Jika Anda hanya ingin mengubah teks untukBarButtonItem kiri yang diperlihatkan oleh pengontrol navigasi bersama dengan tampilan baru, Anda dapat mengubah judul tampilan saat ini sebelum memanggil pushViewController ke teks yang diinginkan dan mengembalikannya ke dalam viewHasDisappered callback untuk ditampilkan di lain waktu tampilan saat ini.

Pendekatan ini menjaga fungsionalitas (popViewController) dan tampilan panah yang ditampilkan tetap utuh.

Ini berfungsi untuk kita setidaknya dengan iOS 12, dibangun dengan Xcode 10.1 ...

Bubu
sumber
Saya kira jawaban ini tidak terkait dengan pertanyaan.
Pierre Espenan