【UIButton】addTargetのSelector経由でString型パラメタを渡す【Swift3.0】

UIButtonの`addTarget`メソッドに指定するファンクションに固有のパラメタを設定するときの話です。数値であれば`tag`プロパティが使えますが、場合によっては文字列を渡したいこともあります。


### 1. もともと持っているプロパティを流用する
accessibilityValueを利用している例。本来の用途と違う使われ方をしちゃうと可読性が落ちますね...

```swift

//
//  ViewController.swift
//

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

    
        let button = UIButton(frame: CGRect(x:0,y:0,width:120,height:30))
        button.center = view.center
        button.layer.backgroundColor = UIColor.red.cgColor
        button.setTitle("私はボタン", for: .normal)
        button.accessibilityValue = "AAAAAAAAAAAAA"
        button.addTarget(self, action: #selector(ViewController.pushButton), for: .touchUpInside)
        
        view.addSubview(button)
    
    }

    func pushButton(_ sender:UIButton) {
        
        print(sender.accessibilityValue) // AAAAAAAAAAAAA
    }
    
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}
```

### 2. UIButtonを継承したサブクラスを作成

一番ポピュラーな方法でしょう。例は固有のプロパティ(stringValue)を追加したMyButtonクラスを作成。

```swift
//
//  ViewController.swift
//

import UIKit

// UIBUttonを継承したクラスを作成
class MyButton:UIButton {
    var stringValue:String?
}

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

    
        let button = MyButton(frame: CGRect(x:0,y:0,width:120,height:30))
        button.center = view.center
        button.layer.backgroundColor = UIColor.red.cgColor
        button.setTitle("私はボタン", for: .normal)
        button.stringValue = "AAAAAAAAAAAAA"
        button.addTarget(self, action: #selector(ViewController.pushButton), for: .touchUpInside)
        
        view.addSubview(button)
    
    }

    func pushButton(_ sender:MyButton) {
        
        print(sender.stringValue) // AAAAAAAAAAAAA
    }
    
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

```

### 3. UIButtonを拡張する

Protocol Extensionを利用して強引にUIButtonに保存型プロパティを追加する方法です。冗長ですが、場合によっては既存のプロジェクトの改修などでは修正量が減らせるかも。

参考：[Swiftのextensionでstored Propertyを追加する？（黒魔術は閉じ込める）](http://www.tokoro.me/2015/11/08/has-associated-objects/)

```swift
//
//  ViewController.swift
//

import UIKit

protocol HasAssociatedObjects {
    var associatedObjects: AssociatedObjects { get }
}

private var AssociatedObjectsKey: UInt8 = 0

extension HasAssociatedObjects where Self: AnyObject {
    
    var associatedObjects: AssociatedObjects {
        guard let associatedObjects = objc_getAssociatedObject(self, &AssociatedObjectsKey) as? AssociatedObjects else {
            let associatedObjects = AssociatedObjects()
            objc_setAssociatedObject(self, &AssociatedObjectsKey, associatedObjects, .OBJC_ASSOCIATION_RETAIN)
            return associatedObjects
        }
        return associatedObjects
    }
    
}

// UIButtonにstoredPropertyを追加！
extension UIButton: HasAssociatedObjects {
    var storedProperty: String? {
        get {
            return self.associatedObjects["HOGE"] as? String
        }
        set {
            self.associatedObjects["HOGE"] = newValue
        }
    }
}

class AssociatedObjects: NSObject {
    
    var dictionary: [String: Any] = [:]
    
    subscript(key: String) -> Any? {
        get {
            return self.dictionary[key]
        }
        set {
            self.dictionary[key] = newValue
        }
    }
    
}

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

    
        let button = UIButton(frame: CGRect(x:0,y:0,width:120,height:30))
        button.center = view.center
        button.layer.backgroundColor = UIColor.red.cgColor
        button.setTitle("私はボタン", for: .normal)
        button.storedProperty = "AAAAAAAAAAAAA"
        button.addTarget(self, action: #selector(ViewController.pushButton), for: .touchUpInside)
        
        view.addSubview(button)
    
    }

    func pushButton(_ sender:UIButton) {
        
        print(sender.storedProperty) // AAAAAAAAAAAAA
    }
    
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}
```

### おまけ

UIButtonのメソッドに、`setValue(_ value: Any?, forKey key: String)`ってのと`value(forKey key: String) -> Any?`ってのを見つけて、「おっ、これキー指定で保存できんのかな？」と思って試してみたらビルドは出来るものの、ランタイム時にアベンドしました。どうやら内部にて使われるものらしく任意に呼び出してはいけないっぽいです。

## 追記　2017/1/6
`setValue(_ value: Any?, forKey key: String)`と`value(forKey key: String) -> Any?`の件ですが、

*Key-Value Coding用のメソッドです。UIButtonのメソッドではなくNSObjectのメソッドになります。例えばUIButtonのtagに数値を入れる時に以下のように使えます。*

```swift

let button = UIButton()
//通常はこう
button.tag = 10

//KVCだとこう
button.setValue(10, forKey: "tag")
```

と[furuyanさん](http://qiita.com/furuyan)に教えていただきました。