@selector () в Swift?

608

Я пытаюсь создать 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 .
Теги:
selector
nstimer

22 ответа

842
Лучший ответ

Сам Swift не использует селекторов - несколько шаблонов проектирования, которые в Objective-C используют селекторы, работают по-разному в Swift. (Например, используйте опциональную цепочку для типов протоколов или is/as вместо respondsToSelector: и используйте закрытие, где только можно, вместо performSelector: для повышения безопасности типа и памяти.)

Но есть еще множество важных API-интерфейсов на основе ObjC, которые используют селекторы, включая таймеры и шаблон цели/действия. 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+ есть специальный синтаксис для пар getter/setter. Например, с учетом 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, поэтому убедитесь, что вы используете правильный API на основе KeyPath вместо селекторов, если это необходимо.

Подробнее о селекторах можно узнать в разделе Взаимодействие с API Objective-C с помощью Swift с Cocoa и Objective-C.

Примечание.. Перед тем, как Swift 2.2, Selector соответствует StringLiteralConvertible, вы можете найти старый код, где голые строки передаются API, которые принимают селектора. Вы хотите запустить "Преобразовать в текущий синтаксис Swift" в Xcode, чтобы получить те, которые используют #selector.

  • 7
    Помещение строки с именем функции сработало, NSSelectorFromString () также работает.
  • 6
    Хотелось бы отметить, что хотя «Взаимодействие с API-интерфейсами Objective-C» есть на сайте, его нет в книге «Язык программирования Swift».
Показать ещё 14 комментариев
81

Вот краткий пример использования класса Selector в Swift:

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   
}

Обратите внимание, что если метод, переданный в виде строки, не работает, он будет работать не во время выполнения, а не во время компиляции и сбой вашего приложения. Будьте осторожны.

  • 12
    что ужасно ... есть что-то типа "NSStringFromSelector"?
  • 11
    не могу поверить, что они предназначены для непроверенных селекторов, так как у objc было это
Показать ещё 4 комментария
42

Кроме того, если ваш (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*/}
  • 0
    @objc работал на меня. Мне не нужно было включать timer: NSTimer в сигнатуру моего метода для его timer: NSTimer .
25

Swift 2. 2+ и Swift 3 Обновление

Используйте новое выражение #selector, которое устраняет необходимость использования строковых литералов, что делает использование менее подверженным ошибкам. Для справки:

Selector("keyboardDidHide:")

становится

#selector(keyboardDidHide(_:))

Смотрите также: Предложение Swift Evolution

Примечание (Swift 4.0):

Если вы используете #selector вам нужно пометить функцию как @objc

Пример:

@objc func something(_ sender: UIButton)

22

Для будущих читателей я обнаружил, что у меня возникла проблема и возникла ошибка unrecognised selector sent to instance, которая была вызвана тем, что цель func была закрыта.

func ДОЛЖЕН быть общедоступным, чтобы его вызывал объект со ссылкой на селектор.

  • 13
    он не должен быть публичным, вы все равно можете хранить метод закрытым, но добавьте objc до его объявления. Пример: @objc private func foo() { ... тогда вы можете использовать "foo" в качестве селектора так, как вам нравится
  • 0
    динамический func foo () {...} также работает
Показать ещё 1 комментарий
19

На всякий случай у кого-то еще была такая же проблема, с которой я столкнулся с NSTimer, где ни один из других ответов не устранил проблему, действительно важно отметить, что если вы используете класс, который не наследует NSObject напрямую или глубоко в иерархия (например, созданные вручную быстрые файлы), ни один из других ответов не будет работать, даже если он указан следующим образом:

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

Без изменения чего-либо другого, кроме как только наследование класса из NSObject, я прекратил получение ошибки "Unrecognized selector" и получил свою логическую работу, как ожидалось.

  • 0
    Проблема с этой альтернативой заключается в том, что вы не можете изменить класс (скажем, ViewController) для наследования от NSObject, учитывая, что вам нужны вещи, реализованные в классе ViewController (например, viewDidLoad ()). Любая идея, как вызвать функцию Swift внутри ViewController с помощью NSTimer? ... e
  • 1
    UIViewController уже наследуется от NSObject, большинство классов, предоставляемых SDK, делают, этот пример для ваших собственных созданных классов, которым требуется функциональность NSTimer ...
18

Swift 4.0

Вы создаете селектор, как показано ниже.

1. добавить событие к кнопке, как:

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

и функция будет как ниже:

@objc func clickedButton(sender: AnyObject) {

}
  • 3
    Вы забыли поставить @objc перед func который требуется в Swift 4.
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 (0.01, target: self, ...), который НЕ работал, тогда как NSTimer.scheduledTimerWithTimeInterval (0.01, ..) DID работал !? Странно, но спасибо @Scooter за ответ!
  • 1
    @ iOS-Coder, просто создающий таймер с инициализатором, не добавляет его в цикл запуска, тогда как scheduledTimerWith... автоматически добавляет его в текущий цикл запуска - так что здесь вообще нет странного поведения;)
Показать ещё 2 комментария
10

Селекторы - это внутреннее представление имени метода в Objective-C. В Objective-C "@selector (methodName)" преобразует исходный код в тип данных SEL. Поскольку вы не можете использовать синтаксис @selector в Swift (здесь находится 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 для более подробной информации о: выражении селектора

  • 0
    Пожалуйста, прекратите это делать. Это никому не помогает. Чем это отличается от Swift 3.1? И почему вы считаете необходимым добавить к этому еще один ответ, когда в нем уже около 20 ответов?
  • 0
    Селектор вызовов отличается в Swift 4. Попробуйте эти ответы в Swift 4 и посмотрите. Ни один из них не будет работать без редактирования. Пожалуйста, не отмечайте какие-либо заявления как спам, не гарантируя их важность
Показать ещё 4 комментария
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
}
  • 0
    было бы лучше и поучительнее, если бы у вас был пример, содержащий два или три аргумента для Swift3 или Swift4. Благодарю.
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
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()

}
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

Использование #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

Я нашел, что многие из этих ответов были полезными, но было непонятно, как это сделать с чем-то, что не было кнопкой. Я добавлял распознаватель жестов в UILabel в swift и боролся так вот, что я нашел, работал у меня после прочтения всего выше:

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

Если "Селектор" был объявлен как:

func labelTapped(sender: UILabel) { }

Обратите внимание, что это общедоступно и я не использую синтаксис Selector(), но это также возможно сделать.

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

вы создаете селектор, как показано ниже.
1.

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

2.

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

Обратите внимание, что синтаксис @selector отсутствует и заменен простой строкой, называя метод вызова. Theres одна область, где мы все можем согласиться с подробностью, полученной в пути. Конечно, если бы мы объявили, что существует целевой метод, называемый 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

Чтобы быть полным, heres flatButtonPressed

func flatButtonPressed(timer: NSTimer) {
}
  • 0
    Есть ли у вас источник для « Обратите внимание, что синтаксис @selector пропал »?
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().

0

Селектор в 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

Для быстрого 3

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

Объявление функции в том же классе

func test()
{
    // my function
} 
Сообщество Overcoder
Наверх
Меню