Swiftの@selector()?


660

私が作成しようとしているNSTimer中をSwift、私はいくつかの問題を抱えています。

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

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


エディターでエラーが発生します。

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

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

私はもう試した:

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

しかし、何も機能せず、参照で解決策を見つけることができません。


11
selector: test()呼び出しtestて、その戻り値をselector引数に渡します。
Michael Dorst、2014年

回答:


930

Swift 自体はセレクターを使用しません— Objective-Cでセレクターを使用するいくつかの設計パターンは、Swiftでは動作が異なります。(たとえば、プロトコルタイプまたはis/ asテストではrespondsToSelector:、オプションのチェーンをの代わりに使用し、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、視認性。

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

セレクターの詳細については、「CocoaおよびObjective-CでのSwiftの使用」の「Objective-C APIの操作」を参照ください。

注: Swift 2.2より前はにSelector準拠しStringLiteralConvertibleていたため、セレクターを取るAPIに裸の文字列が渡される古いコードが見つかる可能性があります。Xcodeで「Convert to Current Swift Syntax」を実行して、を使用するものを取得する必要があり#selectorます。


8
関数名の付いた文字列を入力すると、NSSelectorFromString()も機能します。
Arbitur

7
「Objective-C APIの操作」はウェブサイトに掲載されていますが、「The Swift Programming Language」の本には掲載されていません。

10
これはおそらく、セレクターが引数を取る場合、セレクターの最後に「:」が必要であることを言及する必要があります。(例test()-> "test"&test(this:String)-> "test:")
ダニエルシュラウク

2
CocoaフレームワークはObjective-Cスタイルのメソッド名を想定していることも指摘しておく必要があります。メソッドが引数を取る場合、 ':'が必要です。2つの引数を取る size:andShape:場合、最初の引数に名前が付けられている場合、が必要になる場合がありますWith。つまりinitWithData:func init(Data data: NSData)
JMFR

6
「セレクタ」を文字列として渡すことに関する検証を追加する方法はありますか?IEコンパイラは、スペルミスなどがあると警告を表示します
yo.ian.g 14

86

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

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   
}

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


13
これは恐ろしいことです...「NSStringFromSelector」タイプのものはありますか?
Lena Bru

11
objcがこれをチェックしていないため、チェックされていないセレクター用に設計されているとは信じられません
malhal

4
@malcomhall:@selectorは便利ですが、あなたが思うほど正式ではありません。「宣言されていないセレクター」は、コンパイラーからの単なる警告です。これは、実行時に新しいセレクターを常に導入できるためです。ただし、Swiftでの検証可能/リファクタリング可能なセレクター参照は、を作成するための優れた機能リクエストです。
リクスター2014

2
この回答は役に立ちますが、@ objcを使用した以下の回答がより適切です。
cynistersix 2014年

セレクター文字列を変数またはパラメーターとして渡す場合は、Selector()関数を使用してセレクターをセレクターに知らせる必要があります。感謝
過激な2015

46

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

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

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

そうしないと、実行時に「認識できないセレクタ」エラーが発生します。


3
1.コロン付きのセレクターは引数をとる必要があります2. Appleのドキュメントによれば、タイマーのアクションはNSTimer引数をとる必要があります3. Selectorキーワードは必須ではありません。したがって、この場合の署名は@objc func method(timer: NSTimer) {/*code*/}
Yevhen Dubinin

@objc私のために働いた。timer: NSTimerメソッドシグネチャを呼び出すために含める必要はありませんでした。
Mike Taverne、2015

32

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

新しい#selector式を使用します。これにより、文字列リテラルを使用する必要がなくなり、エラーが発生しにくくなります。参考のために:

Selector("keyboardDidHide:")

なる

#selector(keyboardDidHide(_:))

参照:Swift Evolution Proposal

注意(Swift 4.0):

使用#selectorする場合は、関数を次のようにマークする必要があります@objc

例:

@objc func something(_ sender: UIButton)


26

Swift 4.0

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

1.次のようなボタンにイベントを追加します:

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

そして関数は以下のようになります:

@objc func clickedButton(sender: AnyObject) {

}

4
Swift 4で必要な@objc前に置くのを忘れましたfunc
Vahid Amiri

24

将来の読者のために、私は問題を経験unrecognised selector sent to instanceし、ターゲットをマークすることによって引き起こされたエラーを受け取っていたことがわかりましたfuncをプライベートとして。

func MUSTセレクタを参照してオブジェクトによってコールされる公開され。


13
それはありません持っているあなたはまだプライベートメソッドを保つが、追加することができ、公開するobjcことの宣言の前に。例:好きなだけセレクタとして@objc private func foo() { ...使用できます"foo"
apouche

にすることもできるためinternal、アクセス修飾子を指定しません。:私はこのパターンが多いを使う//MARK: - Selector Methods\n extension MyController {\n func buttonPressed(_ button: UIButton) {
Sajjon

19

他の誰かがNSTimerで同じ問題を抱えていて、他の回答で問題が解決されなかった場合に備えて、NSObjectから直接または階層の深いところから継承されていないクラスを使用している場合、たとえば、手動で作成された迅速なファイル)、次のように指定されている場合でも、他の回答はどれも機能しません:

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

クラスをNSObjectから継承する以外に何も変更せずに、「認識できないセレクター」エラーの発生を停止し、ロジックが期待どおりに機能するようにしました。


この代替手段の問題は、ViewControllerクラスを実装したもの(たとえばviewDidLoad())が必要な場合、NSObjectから継承するようにクラス(ViewControllerとしましょう)を変更できないことです。NSTimerを使用してViewController内でSwift関数を呼び出す方法はありますか?... e
eharo2

1
UIViewControllerはすでにNSObjectを継承しており、SDKによって公開されているほとんどのクラスが継承しています。この例は、NSTimer機能を必要とする独自に作成したクラス用です...
Martin Cazares 14

14

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

セレクターテキスト(テスター:)にコロンを含め、パラメーターはuserInfoに入力します。

関数はNSTimerをパラメーターとして受け取る必要があります。次に、userInfoを抽出して、渡されたパラメーターを取得します。


3
NSTimer.scheduledTimerWithTimeInterval(0.01、..)DIDを使用するのに対し、私は機能しないNSTimer(0.01、target:self、...)を使用していました!?奇妙ですが、@スクーターの回答に感謝します!
iOS-Coder 2015

1
@ iOS-Coderは、イニシャライザでタイマーを作成するだけではそれを実行ループに追加しませんscheduledTimerWith...が、現在の実行ループに自動的に追加します-したがって、ここではまったく奇妙な動作はありません;)
David Ganster

1
@David、ご提案ありがとうございます。私の誤解はSTFWまたはRTFA(Read The F ... ing API)カテゴリのいずれかに属していると思いますか?
iOS-Coder

1
心配する必要はありません。すべてのAPIのすべてのメソッドに関するドキュメントを誰も読む必要はありません;)
David Ganster

10

セレクターは、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")
)

8

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のドキュメントを参照してください。セレクター式


これをやめてください。それは誰にも役立ちません。これはSwift 3.1とどう違うのですか?そして、すでに約20の回答があるのに、なぜこれに別の回答を追加する必要があると思いましたか?
フォグマイスター2017年

セレクターの呼び出しはSwift 4では異なります。Swift4でこれらの答えを試してみてください。これらを編集しないと機能しません。インポテンスを確認せずに、いかなるステートメントもスパムとしてマークしないでください
Krunal

それで、既存の受け入れられた回答を編集できなかった理由はありますか?長い回答リストの最後に追加するのではなく、実際に役立ちます。「編集」ボタンは理由があります。
フォグマイスター2017年

また、これのどの部分がSwift 3と異なるのですか?
フォグマイスター2017年

2
Swift 4のセレクターには、objcタグを追加する必要があります。これが正解です。そして、あなたは他の人の答えを編集してその意味を変えるべきではありません。@Krunalは完全に正しいです。
Unome 2017

6
// 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
}

Swift3またはSwift4についても2つまたは3つの引数を取る例があれば、より優れた教育的なものになります。ありがとう。
nyxee 2017

5
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")
        }
    }
}

4

使用する場合 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)

3

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

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

2。

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

@selector構文がなくなっており、呼び出すメソッドに名前を付ける単純な文字列に置き換えられていることに注意してください。冗長性が邪魔になることに同意できる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) {
}

@selector構文がなくなったことに注意してください」のソースはありますか?
winklerrr 2018年

3

私はこれらの回答の多くが役に立ったと感じましたが、ボタンではない何かを使用してこれを行う方法は明確ではありませんでした。私は迅速かつ苦労してUILabelにジェスチャーレコグナイザーを追加していたので、上記のすべてを読んだ後、これが私のために機能したことがわかりました:

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

「セレクタ」が宣言された場所:

func labelTapped(sender: UILabel) { }

これはパブリックであり、Selector()構文を使用していないことに注意してください。ただし、これも可能です。

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

3

#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!")
    }

2

アクションをトリガーするコントロールをどこに設定するかが重要になることに注意してください。

たとえば、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")
}

1

セレクター構文を呼び出すメソッドで単純な文字列の名前として変更する

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

その後、func test()と入力します。


1

selectorObjective-C世界からの言葉であり、あなたはそれをからSwift呼び出す可能性を持っているObjective-CからSwift それを使うことができますそれはあなたが実行時にいくつかのコードを実行することを可能にします

Swift 2.2構文の前は:

Selector("foo:")

関数名はSelector文字列パラメーター( "foo")として渡されるため、コンパイル時に名前を確認することはできません。その結果、ランタイムエラーが発生する可能性があります。

unrecognized selector sent to instance

Swift 2.2+構文は次のとおりです。

#selector(foo(_:))

Xcodeのオートコンプリートは正しいメソッドを呼び出すのに役立ちます


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.


-1

Swift 3の場合

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

同じクラスでの関数宣言:

@objc func test()
{
    // my function
} 

ターゲットが自分の場合self、セレクターを使用する必要はありません。これで十分です:let timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(test), userInfo: nil, repeats: true)
Mithra Singam
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.