การส่งผ่านพารามิเตอร์ไปยัง addTarget: action: forControlEvents


125

ฉันใช้ addTarget: action: forControlEvents เช่นนี้:

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

และฉันต้องการส่งผ่านพารามิเตอร์ไปยังตัวเลือก "switchToNewsDetails" ของฉัน สิ่งเดียวที่ฉันทำสำเร็จคือส่ง (id) ผู้ส่งโดยเขียน:

action:@selector(switchToNewsDetails:)

แต่ฉันกำลังพยายามส่งผ่านตัวแปรเช่นค่าจำนวนเต็ม เขียนด้วยวิธีนี้ไม่ได้ผล:

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

เขียนด้วยวิธีนี้ไม่ได้ผลเช่นกัน:

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

ความช่วยเหลือใด ๆ จะได้รับการชื่นชม :)


ลายเซ็นวิธีสำหรับ switchToNewsDetails คืออะไร
Aaron Saunders

- (โมฆะ) switchToNewsDetails: (id) ผู้ส่ง; - (โมฆะ) switchToNewsDetails: (int) ฉัน: (id) ผู้ส่ง;
Pierre Espenan

แต่ฉันขึ้นอยู่กับอะไร? มันเฉพาะสำหรับแต่ละปุ่ม? ดูคำตอบของฉัน - คุณสมบัติของแท็กไม่ใช่สิ่งที่คุณต้องการใช่หรือไม่
Vladimir


@PierreEspenan คำตอบที่คุณยอมรับในปัจจุบันอนุญาตให้ส่งจำนวนเต็มผ่านแท็กเท่านั้นซึ่งมีข้อ จำกัด มาก ฉันขอให้คุณเปลี่ยนเป็นคำตอบของฉันซึ่งอนุญาตให้ส่งผ่านวัตถุใด ๆ stackoverflow.com/a/40051706/2057171
Albert Renshaw

คำตอบ:


173
action:@selector(switchToNewsDetails:)

คุณไม่ส่งพารามิเตอร์ไปยังswitchToNewsDetails:วิธีการที่นี่ คุณเพียงแค่สร้างตัวเลือกเพื่อให้ปุ่มสามารถเรียกใช้เมื่อมีการดำเนินการบางอย่างเกิดขึ้น (สัมผัสในกรณีของคุณ) การควบคุมสามารถใช้ตัวเลือก 3 ประเภทเพื่อตอบสนองต่อการกระทำทั้งหมดนี้มีความหมายที่กำหนดไว้ล่วงหน้าของพารามิเตอร์:

  1. โดยไม่มีพารามิเตอร์

    action:@selector(switchToNewsDetails)
  2. โดยมีพารามิเตอร์ 1 ตัวระบุตัวควบคุมที่ส่งข้อความ

    action:@selector(switchToNewsDetails:)
  3. โดยมีพารามิเตอร์ 2 ตัวระบุตัวควบคุมที่ส่งข้อความและเหตุการณ์ที่ทริกเกอร์ข้อความ:

    action:@selector(switchToNewsDetails:event:)

ยังไม่ชัดเจนว่าคุณพยายามทำอะไรกันแน่ แต่เมื่อพิจารณาว่าคุณต้องการกำหนดดัชนีรายละเอียดเฉพาะให้กับแต่ละปุ่มคุณสามารถทำสิ่งต่อไปนี้:

  1. ตั้งค่าคุณสมบัติแท็กให้แต่ละปุ่มเท่ากับดัชนีที่ต้องการ
  2. ในswitchToNewsDetails:วิธีการนี้คุณสามารถรับดัชนีนั้นและเปิด deatails ที่เหมาะสม:

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

4
จะเกิดอะไรขึ้นถ้าเราพยายามส่งผ่านสิ่งอื่นที่ไม่ใช่จำนวนเต็ม? จะเกิดอะไรขึ้นถ้าฉันต้องการส่งผ่านวัตถุเช่นสตริง?
user102008

@user บริบทของคุณคืออะไร? ดูเหมือนว่าคุณจะต้องผ่านมันแยกกัน
Vladimir

ขอบคุณมาก. คุณพบข้อมูลนี้ที่ไหน ฉันมักจะขอบคุณข้อมูลอ้างอิงเพื่อที่บางทีฉันจะได้พบมันในครั้งต่อไป
bearMountain

1
@bearMountain คุณสามารถตรวจสอบลิงค์นี้: developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/…ตัวอย่างเช่น
Vladimir

3
คุณไม่จำเป็นต้องผ่านมันแยกกัน หากคุณผ่าน NSObject ใด ๆ คุณสามารถพูดได้[button.layer setValue:yourObject forKey:@"anyKey"];จากนั้นในวิธีการตรวจสอบ(objectClass *)[button.layer valueForKey:@"anyKey"];มันเหมือนกับเวอร์ชันฟรีมากขึ้นของ.tag
Albert Renshaw

64

เพื่อส่งผ่านพารามิเตอร์ที่กำหนดเองพร้อมกับปุ่มคลิกคุณก็จำเป็นที่จะต้องsubclass UIButton

(ASR เปิดอยู่ดังนั้นจึงไม่มีการเผยแพร่ในโค้ด)

นี่คือmyButton.h

#import <UIKit/UIKit.h>

@interface myButton : UIButton {
    id userData;
}

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

@end

นี่คือmyButton.m

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

การใช้งาน:

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

ฟังก์ชั่นการรับ:

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

หากคุณต้องการให้ฉันเจาะจงมากขึ้นในส่วนใดส่วนหนึ่งของโค้ดด้านบน - อย่าลังเลที่จะถามเกี่ยวกับเรื่องนี้ในความคิดเห็น


ฉันคิดว่านี่เป็นวิธีที่ดีที่สุด
ÇağatayGürtürk

โซลูชันที่หรูหราที่สุด
Victor C.

2
คำตอบที่ดี ... ปรับแต่งได้มาก สิ่งที่ฉันกำลังมองหา MOLODETS! :)
denikov

ขอบคุณสำหรับคำติชม :) ดีใจที่มีประโยชน์สำหรับใครบางคน :)
Yuriy Polezhayev

1
วิธีแก้ปัญหาที่ง่ายและมีประโยชน์ เพียงแค่ให้ฉันเพิ่มที่ว่านี้มีความคล้ายคลึงกับสถานที่ให้บริการในมุมมองของtag Android พวกเขาสามารถจัดเก็บวัตถุใด ๆ และคุณยังสามารถจัดเก็บวัตถุด้วยคีย์เช่นในพจนานุกรม / แผนที่
Ferran Maylinch

19

Target-Action อนุญาตให้ใช้ตัวเลือกการดำเนินการได้สามรูปแบบ:

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

16

ต้องการมากกว่าแค่ (int) ผ่าน. แท็ก? ใช้ KVC!

คุณสามารถส่งผ่านข้อมูลใด ๆ ที่คุณต้องการผ่านอ็อบเจ็กต์ปุ่ม (โดยการเข้าถึง CALayers keyValue dict)


กำหนดเป้าหมายของคุณเช่นนี้ (ด้วยเครื่องหมาย ":")

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

เพิ่มข้อมูลของคุณลงในปุ่มเอง (เช่นเดียวกับ.layerของปุ่มที่เป็น) ดังนี้:

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!

จากนั้นเมื่อแตะปุ่มคุณสามารถตรวจสอบได้ดังนี้:

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

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

}

ไม่มีวิธีการใน UIButton.layer ในการตั้งค่าหรือเพิ่มวัตถุ คุณจะหาทางออกได้จากที่ไหน?
mAc

1
UIButton เป็นประเภทของ UIView UIViews ทั้งหมดมีคุณสมบัติ CALayer .layer CALayer มีพฤติกรรม NSDictionary โปรดทราบว่านี่คือ Objective-C ไม่ใช่ Swift นั่นอาจเป็นปัญหา? นี่คือเอกสารของ Apple เกี่ยวกับการเข้ารหัสคีย์ - ค่า CALayer: developer.apple.com/library/content/documentation/Cocoa/…
Albert Renshaw

1
นี่ควรเป็นคำตอบด้านบน วิธีที่ง่ายที่สุดที่จะทำ!. ขอบคุณ!
danielrosero

1
ควรได้รับคำตอบเป็นวิธีที่ง่ายและชาญฉลาดกว่ามาก
Emon

1
ตอบโจทย์มาก! หวังว่าฉันจะรู้เรื่องนี้มานานแล้ว
John

7

ฉันได้แก้ไขบางส่วนตามข้อมูลด้านบน ฉันเพิ่งตั้ง titlelabel.text เป็นสตริงที่ฉันต้องการส่งผ่านและตั้ง titlelabel.hidden = YES

แบบนี้ :

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

วิธีนี้ไม่จำเป็นต้องมีการสืบทอดหรือหมวดหมู่และไม่มีการรั่วไหลของหน่วยความจำ


5

ฉันกำลังสร้างปุ่มหลายปุ่มสำหรับแต่ละหมายเลขโทรศัพท์ในอาร์เรย์ดังนั้นแต่ละปุ่มจึงต้องใช้หมายเลขโทรศัพท์ที่แตกต่างกันในการโทร ฉันใช้ฟังก์ชัน setTag ในขณะที่ฉันสร้างปุ่มหลายปุ่มภายใน 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];
}

จากนั้นในการโทรของฉัน: วิธีการฉันใช้แบบเดียวกันสำหรับการวนซ้ำและคำสั่ง if เพื่อเลือกหมายเลขโทรศัพท์ที่ถูกต้อง:

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

คุณสามารถปรับปรุงรหัสนี้:- (void)call:(UIButton *)sender { NSString *phoneNumber = _phoneNumbers[i]; NSString *callString = [NSString stringWithFormat:@"telprompt://%@", phoneNumber]; [[UIApplication sharedApplication] openURL:[NSURL URLWithString:callString]]; }
Ladessa

2

เนื่องจากมีหลายวิธีที่กล่าวถึงในที่นี้สำหรับการแก้ปัญหายกเว้นคุณลักษณะหมวดหมู่

ใช้คุณสมบัติหมวดหมู่เพื่อขยายองค์ประกอบที่กำหนด (ในตัว) ไปยังองค์ประกอบที่ปรับแต่งได้ของคุณ

ตัวอย่างเช่น (เช่น):

@interface UIButton (myData)

@property (strong, nonatomic) id btnData;

@end

ในมุมมองของคุณ Controller m

 #import "UIButton+myAppLists.h"

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

จัดการเหตุการณ์:

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

คุณไม่สามารถเพิ่มคุณสมบัติในหมวดหมู่ได้จริงๆ
HotFudgeSunday

2

คุณสามารถแทนที่การดำเนินการเป้าหมายด้วยการปิด (บล็อกใน Objective-C) โดยการเพิ่มตัวช่วยปิดกระดาษห่อหุ้ม (ClosureSleeve) และเพิ่มเป็นอ็อบเจ็กต์ที่เกี่ยวข้องกับตัวควบคุมเพื่อให้ยังคงอยู่ ด้วยวิธีนี้คุณสามารถส่งผ่านพารามิเตอร์ใดก็ได้

สวิฟต์ 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)
    }
}

การใช้งาน:

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

วิธีการด้านบน ( self.switchToNewsDetails(parameter: i)) ยิง 2 ครั้งทำไมช่วยฉันด้วย
Nagendar

รหัสใช้งานได้สำหรับฉัน การทดสอบในใหม่เดี่ยวดู App โครงการpastebin.com/tPaqetMb ดังนั้นปัญหาอาจอยู่ที่รหัสของคุณเช่นโทรbutton.addAction()สองครั้ง เพิ่มเบรกพอยต์button.addActionและในบรรทัดแรกภายในการปิดและพยายามดีบักด้วยตัวเอง
MariánČerný

2

มีอีกวิธีหนึ่งที่คุณจะได้รับ indexPath ของเซลล์ที่คุณกดปุ่ม:

ใช้ตัวเลือกการกระทำตามปกติเช่น:

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

จากนั้นในฟังก์ชันของคุณ:

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

เนื่องจากคุณได้รับ indexPath มันจึงง่ายกว่ามาก


1

ดูความคิดเห็นของฉันด้านบนและฉันเชื่อว่าคุณต้องใช้ NSInvocation เมื่อมีพารามิเตอร์มากกว่าหนึ่งตัว

ข้อมูลเพิ่มเติมเกี่ยวกับ NSInvocation ที่นี่

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


อันที่จริงฉันไม่ต้องการส่งพารามิเตอร์หลายตัวฉันแค่ต้องการจำนวนเต็ม
Pierre Espenan

จริงๆแล้วมีวิธีการ performSelector: withObject: withObject: ที่อนุญาตให้เรียกตัวเลือกที่มีพารามิเตอร์ 2 ตัว แต่สำหรับมากกว่านั้นคุณต้องใช้ NSInvocation
Vladimir

1

สิ่งนี้ช่วยแก้ปัญหาของฉันได้ แต่มันล้มเหลวเว้นแต่ฉันจะเปลี่ยน

action:@selector(switchToNewsDetails:event:)                 

ถึง

action:@selector(switchToNewsDetails: forEvent:)              

0

ฉัน subclassed UIButton ใน CustomButton และฉันเพิ่มคุณสมบัติที่ฉันเก็บข้อมูลของฉัน ดังนั้นฉันจึงเรียก method: (CustomButton *) ผู้ส่งและในวิธีนี้ฉันอ่านเฉพาะ data sender.myproperty ของฉันเท่านั้น

ตัวอย่าง CustomButton:

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

วิธีการดำเนินการ:

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

กำหนดการดำเนินการ

    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

หากคุณต้องการเปลี่ยนข้อความสำหรับ leftBarButtonItem ที่แสดงโดยตัวควบคุมการนำทางพร้อมกับมุมมองใหม่คุณสามารถเปลี่ยนชื่อของมุมมองปัจจุบันก่อนที่จะเรียก pushViewController เป็นข้อความที่ต้องการและเรียกคืนในการเรียกกลับ viewHasDisappered สำหรับการแสดงในอนาคตของ มุมมองปัจจุบัน

วิธีนี้จะคงการทำงาน (popViewController) และลักษณะของลูกศรที่แสดงไว้เหมือนเดิม

อย่างน้อยก็ใช้ได้กับเราด้วย iOS 12 ที่สร้างขึ้นด้วย Xcode 10.1 ...


ฉันไม่คิดว่าคำตอบนี้เกี่ยวข้องกับคำถาม
Pierre Espenan
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.