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

-(void) switchToNewsDetails : (id) 발송 인; -(void) switchToNewsDetails : (int) i : (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/...을 예를 들어
블라디미르

3
별도로 전달하지 않아도됩니다. NSObject를 전달하는 경우, 그냥 말하기 만하면 [button.layer setValue:yourObject forKey:@"anyKey"];됩니다.이 방법 (objectClass *)[button.layer valueForKey:@"anyKey"];에서는 더 자유로운 버전과 같습니다.tag
Albert Renshaw

64

버튼 클릭과 함께 사용자 정의 매개 변수를 전달하려면 UIButton SUBCLASS 만하면 됩니다.

(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ğatay Gürtürk

가장 우아한 솔루션.
Victor C.

2
훌륭한 답변 ... 매우 사용자 정의 가능. 정확히 내가 찾던 것. 몰로 데스! :)
denikov

의견 주셔서 감사합니다 :) 다행, 그것은 누군가에게 유용합니다 :)
Yuriy Polezhayev

1
간단하고 유용한 해결 방법. 이것이 tag안드로이드 뷰속성 과 유사하다는 것을 추가하겠습니다 . 모든 객체를 저장할 수 있습니다. 사전 /지도 에서처럼 키로 객체를 저장할 수도 있습니다 .
Ferran Maylinch

19

Target-Action은 다음과 같은 세 가지 다른 유형의 조치 선택기를 허용합니다.

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

16

.tag를 통한 단순한 (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 유형이며 모든 UIView에는 CALayer .layer 속성이 있습니다. CALayer에는 NSDictionary 동작이 포함되어 있습니다. 이것은 스위프트가 아닌 Objective-C입니다. 문제가 될 수 있습니까? CALayer 키-값 코딩에 관한 Apple의 문서는 다음과 같습니다. 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

배열의 각 전화 번호에 대해 여러 개의 단추를 만들었으므로 각 단추마다 다른 전화 번호가 필요했습니다. for 루프 내에 여러 버튼을 만들 때 setTag 함수를 사용했습니다.

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

솔루션에 대해 여기에 언급 된 많은 방법이 있으므로 카테고리 기능은 제외합니다.

범주 기능을 사용하여 정의 된 (내장) 요소를 사용자 정의 가능한 요소로 확장하십시오.

예를 들어 (ex) :

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

카테고리에 속성을 실제로 추가 할 수는 없습니다.
HotFudge

2

헬퍼 클로저 래퍼 (ClosureSleeve)를 추가하고이를 컨트롤에 연결된 객체로 추가하여 대상 액션을 클로저 (Objective-C의 블록)로 대체 할 수 있습니다. 그렇게하면 모든 매개 변수를 전달할 수 있습니다.

스위프트 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

코드가 나를 위해 작동합니다. 새로운 Single View 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

CustomButton에서 UIButton을 서브 클래 싱하고 데이터를 저장하는 속성을 추가합니다. 그래서 메서드 : (CustomButton *) 보낸 사람을 호출하고 메서드에서 내 데이터 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)과 표시된 화살표 모양을 그대로 유지합니다.

Xcode 10.1로 빌드 된 iOS 12 이상에서 작동합니다 ...


이 답변이 질문과 관련이 있다고 생각하지 않습니다.
Pierre Espenan
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.