Swiftでbutton.addTargetアクションにパラメーターをアタッチする


102

buttonClickedアクションに追加のパラメーターを渡そうとしていますが、Swiftの構文を理解できません。

button.addTarget(self, action: "buttonClicked:", forControlEvents: UIControlEvents.TouchUpInside)

私のbuttonClickedメソッド:

func buttonClicked(sender:UIButton)
{
    println("hello")
}

誰かアイデアはありますか?

ご協力いただきありがとうございます。


1
参照してくださいこのリンク詳細な回答やベストプラクティスのために
sheetal

この問題は、問題のあるアーキテクチャについてすでに説明しているため、適切な回答にはなりません。追加のパラメーターは必要ありません。追加のパラメーターの必要性は、アーキテクチャーの問題です。
2016

WARNING TO INTERNET: please check out my answer at the bottom
Heelis氏、2017年

回答:


170

でカスタムパラメータを渡すことはできませんaddTarget:。1つの選択肢はtag、ボタンのプロパティを設定し、タグに基づいて機能することです。

button.tag = 5
button.addTarget(self, action: "buttonClicked:", 
    forControlEvents: UIControlEvents.TouchUpInside)

またはSwift 2.2以降の場合

button.tag = 5
button.addTarget(self,action:#selector(buttonClicked),
    forControlEvents:.TouchUpInside)

tagプロパティに基づいてロジックを実行します

@objc func buttonClicked(sender:UIButton)
{
    if(sender.tag == 5){

        var abc = "argOne" //Do something for tag 5
    }
    print("hello")
}

4
こんにちは、tableViewのindexPathまたはボタンのセルを取得する場合、それは可能ですか?
NKurapati

1
ヒントは次のとおりです。メソッドをプライベートにしないでください。
OhadM 2016

2
タイプにしかできないようIntです。他には何もありません。
scaryguy

2
行とセクションを一緒に渡す場合はどうなりますか?
Yestay Muratov 2016

3
ちょうどあなたが118 upvotes(とカウント)にもかかわらず、知っている、これはこれを行うには間違った方法である
氏Heelis

79

indexPathやurlStringなどの追加パラメーターをbuttonClickedメソッドに送信する場合は、UIButtonをサブクラス化できます。

class SubclassedUIButton: UIButton {
    var indexPath: Int?
    var urlString: String?
}

アイデンティティインスペクタでボタンのクラスを必ずsubclassedUIButtonに変更してください。sender.indexPathまたはを使用して、buttonClickedメソッド内のパラメーターにアクセスできますsender.urlString

注:ボタンがセル内にある場合、(ボタンが作成される)cellForRowAtIndexPathメソッドでこれらの追加パラメーターの値を設定できます。


5
これが私にとって有効な唯一の方法でした。しかし、私は興味があります、これはアンチパターンですか?
scaryguy

1
これが最良の方法だと思います
Duy Hoang

29

タグを使うと言ってくださった皆さんに感謝しますが、実際にはUIButtonクラスを拡張してそこにオブジェクトを追加するだけで十分です。

タグはこれを回避する絶望的な方法です。このようにUIButtonを拡張します(Swift 4で)

import UIKit
class PassableUIButton: UIButton{
    var params: Dictionary<String, Any>
    override init(frame: CGRect) {
        self.params = [:]
        super.init(frame: frame)
    }

    required init?(coder aDecoder: NSCoder) {
        self.params = [:]
        super.init(coder: aDecoder)
    }
}

その後、あなたの呼び出しは呼び出しになる可能性があります(コロン「:」に注意Selector(("webButtonTouched:"))

let webButton = PassableUIButton(frame: CGRect(x:310, y:40, width:40, height:40))
webButton.setTitle("Visit",for: .normal)
webButton.addTarget(self, action: #selector(YourViewController.webButtonTouched(_:)), for:.touchUpInside)
webButton.params["myvalue"] = "bob"

そして最後にすべてここでそれをキャッチ

@IBAction func webButtonTouched(_ sender: PassableUIButton) {
    print(sender.params["myvalue"] ?? "")
}

これを1回行い、プロジェクト全体で使用します(子クラスに一般的な「オブジェクト」を持たせ、ボタンに好きなものを配置することもできます!)。または、上記の例を使用して、無数のキー/文字列パラメータをボタンに配置します。URL、確認メッセージの方法などを含める場合に非常に役立ちます。

余談ですが、SOコミュニティがこれを理解していることは重要です。何も知らない/教えられていない/要点を逃した驚異的な数のプログラマによって、インターネット全体に悪い慣習がカットアンドペーストされているのです。の概念object extensions


10

Swift 3.0の場合、以下を使用できます

button.addTarget(self, action: #selector(YourViewController.YourMethodName(_:)), for:.touchUpInside)

func YourMethodName(_ sender : UIButton) {
    print(sender.tag)

}

9

Swift 4.2

結果:

testButton.on(.touchUpInside) { (sender, event) in
    // You can use any reference initialized before the code block here
    // You can access self by adding [weak self] before (sender, event)
    // You can then either make self strong by using a guard statement or use a optional operator (?)
    print("user did press test button")
}

このファイルで、と呼ばれる完了ハンドラーにバインドするUIButton+Events.swiftための拡張メソッドを作成しました。UIButtonUIControl.EventEventHandler

import UIKit

fileprivate var bindedEvents: [UIButton:EventBinder] = [:]

fileprivate class EventBinder {

    let event: UIControl.Event
    let button: UIButton
    let handler: UIButton.EventHandler
    let selector: Selector

    required init(
        _ event: UIControl.Event,
        on button: UIButton,
        withHandler handler: @escaping UIButton.EventHandler
    ) {
        self.event = event
        self.button = button
        self.handler = handler
        self.selector = #selector(performEvent(on:ofType:))
        button.addTarget(self, action: self.selector, for: event)
    }

    deinit {
        button.removeTarget(self, action: selector, for: event)
        if let index = bindedEvents.index(forKey: button) {
            bindedEvents.remove(at: index)
        }
    }
}

private extension EventBinder {

    @objc func performEvent(on sender: UIButton, ofType event: UIControl.Event) {
        handler(sender, event)
    }
}

extension UIButton {

    typealias EventHandler = (UIButton, UIControl.Event) -> Void

    func on(_ event: UIControl.Event, handler: @escaping EventHandler) {
        bindedEvents[self] = EventBinder(event, on: self, withHandler: handler)
    }
}

イベントをバインドするためにカスタムクラスを使用した理由は、後でボタンが初期化されたときに参照を破棄できるようにするためです。これにより、メモリリークの発生を防ぐことができます。UIButtonプロパティやdeinitメソッドを実装することが許可されていないため、これは拡張機能内では不可能でした。


2

あなたが私のようなボタンのループを持っているなら、あなたはこのようなものを試すことができます

var buttonTags:[Int:String]? // can be [Int:Any]
let myArray = [0:"a",1:"b"]
for (index,value) in myArray {

     let button = // Create a button

     buttonTags?[index] = myArray[index]
     button.tag = index
     button.addTarget(self, action: #selector(buttonAction(_:)), for: .touchDown)

}
@objc func buttonAction(_ sender:UIButton) {

    let myString = buttonTags[sender.tag]

}

2

Swift 4.0コード(ここでまた行きます)

呼び出されたアクションは、関数を目的のC言語にエクスポートするための迅速な関数の構文であるため、このようにマークする必要があります。

@objc func deleteAction(sender: UIButton) {
}

機能するボタンを作成します。

let deleteButton = UIButton(type: .roundedRect)
deleteButton.setTitle("Delete", for: [])
deleteButton.addTarget(self, action: #selector( 
MyController.deleteAction(sender:)), for: .touchUpInside)

共有いただきありがとうございます。これは機能しますが、@ objcでマークするのは直感的ではありません。この理由を説明できますか?
rawbee 2017年

@rawbeeこれは直感に反することに同意します。この質問は、より多くのバックグラウンドを持っていますstackoverflow.com/questions/44390378/...
Mikrasya

1

Swift 5.0コード

私はtheButton.tagを使用しますが、オプションの種類がたくさんある場合、非常に長いスイッチケースになります。

theButton.addTarget(self, action: #selector(theFunc), for: .touchUpInside) 
theButton.frame.name = "myParameter"

@objc func theFunc(sender:UIButton){ 
print(sender.frame.name)
}

0

Swift 2.X以降の場合

button.addTarget(self,action:#selector(YourControllerName.buttonClicked(_:)),
                         forControlEvents:.TouchUpInside)

0

Swift 3.0コード

self.timer = Timer.scheduledTimer(timeInterval: timeInterval, target: self, selector:#selector(fetchAutocompletePlaces(timer:)), userInfo:[textView.text], repeats: true)

func fetchAutocompletePlaces(timer : Timer) {

  let keyword = timer.userInfo
}

'userinfo'で値を送信し、関数のパラメーターとして使用できます。


0

これはより重要なコメントです。そのまま使用できるsytanxのリファレンスを共有する。ハックソリューションについては、他の回答を参照してください。

Appleのドキュメントによれば、アクションメソッド定義はこれら3つのいずれかでなければなりません。それ以外のものは受け入れられません。

@IBAction func doSomething()
@IBAction func doSomething(sender: UIButton)
@IBAction func doSomething(sender: UIButton, forEvent event: UIEvent)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.