web-dev-qa-db-ja.com

Swiftでは@selector()?

NSTimerSwiftを作成しようとしていますが、いくつか問題があります。

NSTimer(timeInterval: 1, target: self, selector: test(), userInfo: nil, repeats: true)

test()は同じクラスの関数です。


エディタにエラーが表示されます。

指定された引数を受け入れる 'init'のオーバーロードが見つかりませんでした

selector: test()selector: nilに変更すると、エラーは消えます。

私はもう試した:

  • selector: test()
  • selector: test
  • selector: Selector(test())

しかし何もうまくいきません、そして私は参考文献の中に解決策を見つけることができません。

625
Arbitur

Swiftitselfはセレクターを使用しません。Objective-Cでセレクターを使用するいくつかのデザインパターンは、Swiftで異なる動作をします。 (たとえば、プロトコルタイプでオプションのチェーンを使用するか、respondsToSelector:の代わりにis/asテストを使用し、performSelector:の代わりにできる限りクロージャーを使用して、タイプ/メモリの安全性を高めます)

ただし、タイマーやターゲット/アクションパターンなど、セレクターを使用する多くの重要なObjCベースのAPIがまだあります。 Swiftは、これらを操作するためのSelectorタイプを提供します。 (SwiftはObjCのSELタイプの代わりにこれを自動的に使用します。)

Swift 2.2(Xcode 7.3)以降(Swift 3/Xcode 8およびSwift 4/Xcode 9を含む):

#selector式を使用して、Swift関数タイプからSelectorを構築できます。

let timer = Timer(timeInterval: 1, target: object,
                  selector: #selector(MyClass.test),
                  userInfo: nil, repeats: false)
button.addTarget(object, action: #selector(MyClass.buttonTapped),
                 for: .touchUpInside)
view.perform(#selector(UIView.insertSubview(_:aboveSubview:)),
             with: button, with: otherButton)

このアプローチの素晴らしいところは?関数参照はSwiftコンパイラーによってチェックされるため、#selector式は、実際に存在し、セレクターとしての使用に適格なクラス/メソッドのペアでのみ使用できます(以下の「セレクターの可用性」を参照) )。 Swift 2.2+関数タイプの命名規則 に従って、必要に応じて特定の関数参照のみを自由に作成することもできます。

(これは、ObjCの@selector()ディレクティブよりも実際に改善されています。コンパイラの-Wundeclared-selectorチェックは、名前付きセレクターの存在のみを検証するためです。Swift関数参照は、#selectorクラス内で、署名を入力します。)

#selector式に渡す関数参照には、いくつかの追加の警告があります。

  • 同じベース名を持つ複数の関数は、前述の 関数参照の構文 (例:insertSubview(_:at:) vs insertSubview(_:aboveSubview:))を使用して、パラメーターラベルで区別できます。しかし、関数にパラメーターがない場合、それを明確にする唯一の方法は、関数の型シグネチャでasキャストを使用することです(例:foo as () -> () vs foo(_:))。
  • Swift 3.0+には、プロパティゲッター/セッターペア用の特別な構文があります。たとえば、var foo: Intを指定すると、#selector(getter: MyClass.foo)または#selector(setter: MyClass.foo)を使用できます。

一般的注意事項:

#selectorが機能しない場合、および命名:セレクターを作成するための関数参照がない場合があります(たとえば、ObjCランタイムに動的に登録されたメソッドを使用)。その場合、文字列からSelectorを作成できます。 Selector("dynamicMethod:") —コンパイラーの妥当性検査は失われますが。その場合、各パラメーターのコロン(:)を含むObjC命名規則に従う必要があります。

セレクタの可用性:セレクタによって参照されるメソッドは、ObjCランタイムに公開する必要があります。 Swift 4では、ObjCに公開されるすべてのメソッドの宣言の前に@objc属性を付ける必要があります。 (以前のバージョンでは、場合によってはその属性を無料で取得していましたが、今では明示的に宣言する必要があります。)

privateシンボルもランタイムに公開されないことに注意してください。メソッドには少なくともinternal可視性が必要です。

キーパス:これらはセレクターに関連していますが、セレクターとはまったく同じではありません。 Swift 3にもこれらの特別な構文があります。 chris.valueForKeyPath(#keyPath(Person.friends.firstName))。詳細については、 SE-0062 を参照してください。さらに多くの KeyPath Swift 4にあるもの なので、適切な場合はセレクターの代わりに正しいKeyPathベースのAPIを使用していることを確認してください。

セレクタの詳細については、 Objective-C APIとの対話 inUsing Swift with Cocoa and Objective-Cを参照してください。

注:Swift 2.2より前、SelectorStringLiteralConvertibleに準拠していたため、古いコードを見つけることができます。セレクターを使用するAPIに裸の文字列が渡されます。 Xcodeで「現在のSwift構文に変換」を実行して、#selectorを使用するものを取得する必要があります。

915
rickster

SwiftでSelectorクラスを使用する方法の簡単な例を示します。

override func viewDidLoad() {
    super.viewDidLoad()

    var rightButton = UIBarButtonItem(title: "Title", style: UIBarButtonItemStyle.Plain, target: self, action: Selector("method"))
    self.navigationItem.rightBarButtonItem = rightButton
}

func method() {
    // Something cool here   
}

文字列として渡されたメソッドが機能しない場合は、コンパイル時ではなく実行時に失敗し、アプリケーションがクラッシュします。注意してください

82
Oscar Swanros

また、(Swift)クラスがObjective-Cクラスから派生していない場合は、ターゲットメソッド名の文字列の最後にコロンを付け、ターゲットメソッドで@objcプロパティを使用する必要があります。

var rightButton = UIBarButtonItem(title: "Title", style: UIBarButtonItemStyle.Plain, target: self, action: Selector("method"))

@objc func method() {
    // Something cool here   
} 

それ以外の場合は、実行時に "認識できないセレクタ"エラーが発生します。

46
user3771857

Swift 2.2以降およびSwift 3アップデート

新しい#selector式を使用します。これにより、文字列リテラルを使用する必要がなくなり、使用率が低下する可能性が少なくなります。参考のため:

Selector("keyboardDidHide:")

になる

#selector(keyboardDidHide(_:))

また見なさい: 速い進化の提案

注(Swift 4.0):

#selectorを使用する場合は、関数を@objcとしてマークする必要があります。

例:

@objc func something(_ sender: UIButton)

30
Kyle Clegg

将来の読者のために、私は問題を経験し、ターゲットfuncを非公開としてマークすることによって引き起こされるunrecognised selector sent to instanceエラーを得ていたことを知りました。

funcMUSTは、セレクタへの参照を持つオブジェクトによって呼び出されるように一般に公開されている必要があります。

23
Rob Sanders

Swift 4.0

以下のようにセレクタを作成します。

1.イベントをボタンに追加します。

button.addTarget(self, action: #selector(clickedButton(sender:)), for: UIControlEvents.touchUpInside)

そして機能は以下のようになります。

@objc func clickedButton(sender: AnyObject) {

}
21
pravin salame

他の誰かが私と同じ問題を抱えていて他の答えで問題が解決されない場合に備えて、NSObjectから直接継承していないクラスや階層の奥深くまで継承していないクラスを使用している場合たとえば、手動で作成されたSwiftファイル)、次のように指定されていても他の答えはどれもうまくいきません。

let timer = NSTimer(timeInterval: 1, target: self, selector: "test", 
                    userInfo: nil, repeats: false)
func test () {}

単にクラスをNSObjectから継承させる以外に何も変更することなく、「認識されないセレクタ」エラーが発生するのをやめ、予想通りにロジックを動作させることができました。

19
Martin Cazares

NSTimerから関数にパラメータを渡したい場合は、これがあなたの解決策です:

var somethingToPass = "It worked"

let timer = NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: "tester:", userInfo: somethingToPass, repeats: false)

func tester(timer: NSTimer)
{
    let theStringToPrint = timer.userInfo as String
    println(theStringToPrint)
}

コロンをセレクターテキスト(tester :)に含めると、あなたのパラメータはuserInfoに入ります。

あなたの関数はパラメータとしてNSTimerを取ります。その後、userInfoを抽出して、渡されたパラメータを取得します。

14
Scooter

セレクタはObjective-Cのメソッド名の内部表現です。 Objective-Cでは、 "@selector(methodName)"はソースコードのメソッドをSELのデータ型に変換します。 Swiftでは@selector構文は使用できないため(ricksterがその場にあります)、メソッド名を直接Stringオブジェクトとして手動で指定するか、StringオブジェクトをSelector型に渡す必要があります。これが一例です。

var rightBarButton = UIBarButtonItem(
    title: "Logout", 
    style: UIBarButtonItemStyle.Plain, 
    target: self, 
    action:"logout"
)

または

var rightBarButton = UIBarButtonItem(
    title: "Logout", 
    style: UIBarButtonItemStyle.Plain, 
    target: self, 
    action:Selector("logout")
)
10
Jon Tsiros

Swift 4.1
タップジェスチャーのサンプルとは

let gestureRecognizer = UITapGestureRecognizer()
self.view.addGestureRecognizer(gestureRecognizer)
gestureRecognizer.addTarget(self, action: #selector(self.dismiss(completion:)))

// Use destination 'Class Name' directly, if you selector (function) is not in same class.
//gestureRecognizer.addTarget(self, action: #selector(DestinationClass.dismiss(completion:)))


@objc func dismiss(completion: (() -> Void)?) {
      self.dismiss(animated: true, completion: completion)
}

詳細については、Appleのドキュメントを参照してください。 セレクタ式

8
Krunal
// for Swift 2.2
// version 1
buttton.addTarget(self, action: #selector(ViewController.tappedButton), forControlEvents: .TouchUpInside)
buttton.addTarget(self, action: #selector(ViewController.tappedButton2(_:)), forControlEvents: .TouchUpInside)

// version 2
buttton.addTarget(self, action: #selector(self.tappedButton), forControlEvents: .TouchUpInside)
buttton.addTarget(self, action: #selector(self.tappedButton2(_:)), forControlEvents: .TouchUpInside)

// version 3
buttton.addTarget(self, action: #selector(tappedButton), forControlEvents: .TouchUpInside)
buttton.addTarget(self, action: #selector(tappedButton2(_:)), forControlEvents: .TouchUpInside)

func tappedButton() {
  print("tapped")
}

func tappedButton2(sender: UIButton) {
  print("tapped 2")
}

// Swift 3.x
button.addTarget(self, action: #selector(tappedButton(_:)), for: .touchUpInside)

func tappedButton(_ sender: UIButton) {
  // tapped
}

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

func tappedButton(_ sender: UIButton, _ event: UIEvent) {
  // tapped
}
6
John Lee
Create Refresh control using Selector method.   
    var refreshCntrl : UIRefreshControl!
    refreshCntrl = UIRefreshControl()
    refreshCntrl.tintColor = UIColor.whiteColor()
    refreshCntrl.attributedTitle = NSAttributedString(string: "Please Wait...")
    refreshCntrl.addTarget(self, action:"refreshControlValueChanged", forControlEvents: UIControlEvents.ValueChanged)
    atableView.addSubview(refreshCntrl)

//リフレッシュ制御方法

func refreshControlValueChanged(){
    atableView.reloadData()
    refreshCntrl.endRefreshing()

}
5

Swift 3.0が公開されたので、適切なtargetActionを宣言することはもう少し微妙です。

class MyCustomView : UIView {

    func addTapGestureRecognizer() {

        // the "_" is important
        let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(MyCustomView.handleTapGesture(_:)))
        tapGestureRecognizer.numberOfTapsRequired = 1
        addGestureRecognizer(tapGestureRecognizer)
    }

    // since Swift 3.0 this "_" in the method implementation is very important to 
    // let the selector understand the targetAction
    func handleTapGesture(_ tapGesture : UITapGestureRecognizer) {

        if tapGesture.state == .ended {
            print("TapGesture detected")
        }
    }
}
5
LukeSideWalker

performSelector()を使うとき

あなたのメソッド(セレクタにマッチする)は/addtarget()/NStimer.scheduledTimerWithInterval()メソッドとしてマークされるべきです

@objc
For Swift 2.0:
    {  
        //...
        self.performSelector(“performMethod”, withObject: nil , afterDelay: 0.5)
        //...


    //...
    btnHome.addTarget(self, action: “buttonPressed:", forControlEvents: UIControlEvents.TouchUpInside)
    //...

    //...
     NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector : “timerMethod”, userInfo: nil, repeats: false)
    //...

}

@objc private func performMethod() {
…
}
@objc private func buttonPressed(sender:UIButton){
….
}
@objc private func timerMethod () {
….
}

Swift 2.2では、文字列とセレクタ名の代わりに '#selector()'を書く必要があるので、スペルミスとそれによるクラッシュの可能性はもうありません。下記は例です

self.performSelector(#selector(MyClass.performMethod), withObject: nil , afterDelay: 0.5)
4
sschunara

#selector を使用すると、コンパイル時にコードをチェックして、呼び出したいメソッドが実際に存在することを確認します。さらに、この方法が存在しない場合は、コンパイルエラーが発生します。Xcodeはアプリケーションの構築を拒否し、そのため別のバグの原因を排除することを禁止します。

override func viewDidLoad() {
        super.viewDidLoad()

        navigationItem.rightBarButtonItem =
            UIBarButtonItem(barButtonSystemItem: .Add, target: self,
                            action: #selector(addNewFireflyRefernce))
    }

    func addNewFireflyReference() {
        gratuitousReferences.append("Curse your sudden but inevitable betrayal!")
    }
3
Swift Developer

私はこれらの答えの多くが有用であると思いました、しかしそれがボタンではなかった何かでこれをする方法を明らかにしませんでした。私はSwiftのUILabelにジェスチャー認識機能を追加して苦労したので、上記のすべてを読んだ後に私がうまくいったのは次のとおりです。

let tapRecognizer = UITapGestureRecognizer(
            target: self,
            action: "labelTapped:")

「セレクタ」は次のように宣言されています。

func labelTapped(sender: UILabel) { }

これは一般に公開されているので、私はSelector()構文を使用していませんが、これを実行することも可能です。

let tapRecognizer = UITapGestureRecognizer(
            target: self,
            action: Selector("labelTapped:"))
3
cynistersix

以下のようにセレクタを作成します。
1。

UIBarButtonItem(
    title: "Some Title",
    style: UIBarButtonItemStyle.Done,
    target: self,
    action: "flatButtonPressed"
)

2。

flatButton.addTarget(self, action: "flatButtonPressed:", forControlEvents: UIControlEvents.TouchUpInside)

@selector構文は廃止され、呼び出すメソッドの名前を表す単純なStringに置き換えられました。私たち全員が邪魔になった冗長性に同意できる分野が1つあります。もちろん、flatButtonPressedというターゲットメソッドがあると宣言した場合は、次のように記述します。

func flatButtonPressed(sender: AnyObject) {
  NSLog("flatButtonPressed")
}

タイマーを設定します。

    var timer = NSTimer.scheduledTimerWithTimeInterval(1.0, 
                    target: self, 
                    selector: Selector("flatButtonPressed"), 
                    userInfo: userInfo, 
                    repeats: true)
    let mainLoop = NSRunLoop.mainRunLoop()  //1
    mainLoop.addTimer(timer, forMode: NSDefaultRunLoopMode) //2 this two line is optinal

完全にするために、これがflatButtonPressedです。

func flatButtonPressed(timer: NSTimer) {
}
3
Daxesh Nagar

アクションをトリガーするコントロールをどこに設定するかをメモしておくと便利です。

たとえば、UIBarButtonItemを設定するときに、viewDidLoad内にボタンを作成する必要があります。そうしないと、認識されないセレクタ例外が発生します。

override func viewDidLoad() {
    super.viewDidLoad() 

    // add button
    let addButton = UIBarButtonItem(image: UIImage(named: "746-plus-circle.png"), style: UIBarButtonItemStyle.Plain, target: self, action: Selector("addAction:"))
    self.navigationItem.rightBarButtonItem = addButton
}

func addAction(send: AnyObject?) {     
    NSLog("addAction")
}
2

セレクタ構文を呼び出すメソッド内の単純な文字列命名として変更します

var timer1 : NSTimer? = nil
timer1= NSTimer(timeInterval: 0.1, target: self, selector: Selector("test"), userInfo: nil, repeats: true)

その後、func test()と入力してください。

1
Thar Htet

Swift 4のセレクター:

button.addTarget(self, action: #selector(buttonTapped(sender:)), for: UIControlEvents.touchUpInside)
0

Swift 3の場合

//タイマーを作成するためのサンプルコード

Timer.scheduledTimer(timeInterval: 1, target: self, selector: (#selector(updateTimer)), userInfo: nil, repeats: true)

WHERE
timeInterval:- Interval in which timer should fire like 1s, 10s, 100s etc. [Its value is in secs]
target:- function which pointed to class. So here I am pointing to current class.
selector:- function that will execute when timer fires.

func updateTimer(){
    //Implemetation 
} 

repeats:- true/false specifies that timer should call again n again.
0
CrazyPro007

Swift 3の場合

let timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(self.test), userInfo: nil, repeats: true)

関数宣言同じクラス内:

@objc func test()
{
    // my function
} 
0
Mustajab Jafry