はじめに
昔からそうだったのかもしれませんが(addTarget使ったの初めてだから以前をしらない ^^;)、
Xcode6 Beta6 で UIButton などに addTarget するActionに @IBAction
が無いとエラーになりました。
追加検証も御覧ください
現象
func someMethod() {
btnRight.addTarget(self, action: "onBtnRight:", forControlEvents: .TouchUpInside)
}
func onBtnRight(sender: AnyObject) {
...
}
と書いてたら、
2014-08-22 16:16:15.265 hogehoge[33698:60b] *** NSForwarding: warning: object 0x7fd9dac1d2c0 of class '_TtC6hogehoge18TileViewController' does not implement methodSignatureForSelector: -- trouble ahead
Unrecognized selector -[hogehoge.TileViewController onBtnRight:]
となります。 よく コロン :
を忘れて action: "onBtnRight"
とやってしまったときのような症状です。
解決?
謎だなーと思って調べてもよくわからず。なんとなく @IBAction
を試しに付けてみたら、ちゃんとActionが呼ばれました。
func someMethod() {
btnRight.addTarget(self, action: "onBtnRight:", forControlEvents: .TouchUpInside)
}
@IBAction func onBtnRight(sender: AnyObject) {
...
}
さいごに
@IBAction
は InterfaceBuilder 専用のMarkerだと思っていたんだけど。。。
何かの間違い(バグ)かもしれないし、昔からこうなのかもしれないですが、ちょっと意外でわからなかった…
(2014/8/23) 追加検証・1
@takabosoft さんからコメントを頂いたので追加検証をしてみました。
コメントで頂いたようによくある使い方だとそのような問題はおきませんでした。
結論としては、Target Objectがポイントとなるようで、 TargetがNSObject的な性質を持っていれば問題ない ようです。
どうも SelectorでActionを指定するようなObjectはそうみたいです。
再現しないコード
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
var btn = UIButton.buttonWithType(.System) as UIButton
btn.frame = CGRectMake(0, 0, 100, 100)
btn.setTitle("TEST", forState: .Normal)
btn.addTarget(self, action: "didTouchUpInside:", forControlEvents: .TouchUpInside)
view.addSubview(btn)
}
func didTouchUpInside(sender: AnyObject) {
println("called!")
}
}
再現するコード
このようなコードだと再現しました(落ちました)。 Target Object(Hoge object)が問題みたいです。
import UIKit
class ViewController: UIViewController {
var hoge: Hoge = Hoge()
override func viewDidLoad() {
super.viewDidLoad()
var btn = UIButton.buttonWithType(.System) as UIButton
btn.frame = CGRectMake(0, 0, 100, 100)
btn.setTitle("TEST", forState: .Normal)
btn.addTarget(self, action: "didTouchUpInside:", forControlEvents: .TouchUpInside)
view.addSubview(btn)
hoge.someMethod(btn)
}
func didTouchUpInside(sender: AnyObject) {
println("called!")
}
}
class Hoge {
func someMethod(btn: UIButton) {
btn.addTarget(self, action: "didTouchUpInside:", forControlEvents: .TouchUpInside)
}
func didTouchUpInside(sender: AnyObject) {
println("222222222222")
}
}
再現しないようにするには・その1
@IBAction
を付ける
class Hoge {
func someMethod(btn: UIButton) {
btn.addTarget(self, action: "didTouchUpInside:", forControlEvents: .TouchUpInside)
}
@IBAction func didTouchUpInside(sender: AnyObject) {
println("222222222222")
}
}
再現しないようにするには・その2
HogeがNSObject
を継承する。
class Hoge : NSObject {
func someMethod(btn: UIButton) {
btn.addTarget(self, action: "didTouchUpInside:", forControlEvents: .TouchUpInside)
}
func didTouchUpInside(sender: AnyObject) {
println("222222222222")
}
}
再現しないようにするには・その3
@objc class Hoge
とする。
@objc class Hoge {
func someMethod(btn: UIButton) {
btn.addTarget(self, action: "didTouchUpInside:", forControlEvents: .TouchUpInside)
}
func didTouchUpInside(sender: AnyObject) {
println("222222222222")
}
}
(2014/8/23) 追加検証・2
NSNotificationCenter だとどうなるのだろう、と思って2行追加しました。
やはりこれだと落ちるみたいです。ただ、上記のように NSObject
や@objc
を付けると落ちませんでした。@objc
を付けるのはclass
の前でなくfunc
のところでも良いようです。
Selector周りはそういう動作なのかもしれません。
@objc func didTouchUpInside(sender: AnyObject) {
println("222222222222")
}
import UIKit
class ViewController: UIViewController {
var hoge: Hoge = Hoge()
override func viewDidLoad() {
super.viewDidLoad()
var btn = UIButton.buttonWithType(.System) as UIButton
btn.frame = CGRectMake(0, 0, 100, 100)
btn.setTitle("TEST", forState: .Normal)
btn.addTarget(self, action: "didTouchUpInside:", forControlEvents: .TouchUpInside)
view.addSubview(btn)
hoge.someMethod(btn)
// この2行を追加
NSNotificationCenter.defaultCenter().addObserver(hoge, selector: "didTouchUpInside:", name: "Hoge", object: nil)
NSNotificationCenter.defaultCenter().postNotificationName("Hoge", object: self)
}
func didTouchUpInside(sender: AnyObject) {
println("called!")
}
}
class Hoge {
func someMethod(btn: UIButton) {
btn.addTarget(self, action: "didTouchUpInside:", forControlEvents: .TouchUpInside)
}
func didTouchUpInside(sender: AnyObject) {
println("222222222222")
}
}