addTarget:action:forControlEventsにパラメーターを渡す


125

次のようにaddTarget:action:forControlEventsを使用しています。

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

パラメータをセレクタ「switchToNewsDetails」に渡したいのですが。私が成功した唯一のことは、(id)senderを次のように渡すことです。

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)sender; -(void)switchToNewsDetails:(int)i:(id)sender;
Pierre Espenan、

しかし、何に依存していますか?各ボタンに固有ですか?私の答えを見てください-タグのプロパティはあなたが必要とするものではありませんか?
ウラジミール


@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

@ユーザーあなたのコンテキストは何ですか?個別に渡す必要があるようです
ウラジミール

どうもありがとう。このデータはどこで見つけましたか?多分私はそれを次回自分で見つけることができるように、私は常に参照を感謝しています。
bearMountain 2012年

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

ボタンのクリックとともにカスタムパラメータ渡すには、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

最もエレガントなソリューション。
ビクターC. 14

2
すばらしい答え...非常にカスタマイズ可能です。まさに私が探していたもの。モロデッツ!:)
denikov 2014年

フィードバックをありがとう:)うれしい、誰かに役立つこと:)
Yuriy Polezhayev

1
シンプルで便利な回避策。これはtagAndroidビューのプロパティに似ていることを付け加えておきます。それらはあらゆるオブジェクトを保存できます。また、辞書やマップのように、キーでオブジェクトを保存することもできます。
Ferran Maylinch

19

Target-Actionでは、3つの異なる形式のアクションセレクターを使用できます。

- (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 2017年

1
UIButtonはUIViewの一種であり、すべてのUIViewにCALayer .layerプロパティがあります。CALayerにはNSDictionaryの動作が含まれています。これはSwiftではなくObjective-Cであることに注意してください。ここではコーディングのCALayerのキーと値のAppleのドキュメントは以下のとおりです。developer.apple.com/library/content/documentation/Cocoa/...
アルバート・レンショウ

1
これが一番の答えです。はるかに簡単な方法です!。ありがとう!
ダニエルロセロ2018

1
それは受け入れられるべき答えです。それははるかに簡単でスマートな方法です。
Emon

1
正解です。私がずっと前にこれを知っていたらよかったのに。
ジョン

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

次に、私の呼び出し:メソッドで同じforループと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]]; }
ラデッサ2014

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

カテゴリにプロパティを実際に追加することはできません。
HotFudgeSunday、2015年

2

ヘルパークロージャーラッパー(ClosureSleeve)を追加し、関連付けられたオブジェクトとしてコントロールに追加することで、target-actionをクロージャー(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

コードは私にとってはうまくいきます。新しいシングルビューアプリプロジェクトpastebin.com/tPaqetMbでテストされています。したがって、問題はおそらくbutton.addAction()2回呼び出すようなコードにあります。button.addActionクロージャー内の最初の行にブレークポイントを追加し、自分でデバッグしてみます。
マリアンチェルニー

2

ボタンが押されたセルのindexPathを取得するもう1つの方法があります。

次のような通常のアクションセレクタを使用します。

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

そして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
    ..
}

indexPathを取得するので、はるかに単純になります。


1

上記の私のコメントを参照してください。複数のパラメーターがある場合は、NSInvocationを使用する必要があると思います。

NSInvocationの詳細はこちら

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


実際には、いくつかのパラメーターを渡したくありません。ただ整数が必要です。
Pierre Espenan、

実際、performSelector:withObject:withObject:メソッドがあり、2つのパラメーターでセレクターを呼び出すことができます。しかし、もっと必要なのはNSInvocationが必要です
ウラジミール

1

これで問題は解決しましたが、変更しない限りクラッシュしました

action:@selector(switchToNewsDetails:event:)                 

action:@selector(switchToNewsDetails: forEvent:)              

0

CustomButtonでUIButtonをサブクラス化し、データを格納するプロパティを追加します。だから私はメソッドを呼び出します:(CustomButton *)senderとメソッドで私は自分のデータ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.