@selector() в Swift?

Я пытаюсь создать 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())

Но ничего не работает, и я не могу найти решение в ссылках.

24 ответа

Решение

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

Но есть еще ряд важных API на основе ObjC, которые используют селекторы, включая таймеры и шаблон назначения / действия. Свифт обеспечивает 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 check проверяет только то, что указанный селектор существует. Ссылка на функцию Swift, которую вы передаете #selector проверяет существование, членство в классе и тип подписи.)

Есть несколько дополнительных предостережений для ссылок на функции, которые вы передаете #selector выражение:

  • Несколько функций с одним и тем же базовым именем могут различаться по меткам их параметров с использованием вышеупомянутого синтаксиса для ссылок на функции (например, insertSubview(_:at:) против insertSubview(_:aboveSubview:)). Но если функция не имеет параметров, единственный способ избавиться от нее - это использовать as приведение с подписью типа функции (например, foo as () -> () против 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, поэтому убедитесь, что вы используете правильный API на основе KeyPath вместо селекторов, если это необходимо.

Подробнее о селекторах читайте в разделе " Взаимодействие с API-интерфейсами Objective-C" в разделе " Использование Swift с какао и Objective-C".

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

Вот быстрый пример того, как использовать 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   
}

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

Кроме того, если ваш (Swift) класс не происходит от класса Objective-C, то у вас должно быть двоеточие в конце строки имени целевого метода, и вы должны использовать свойство @objc с вашим целевым методом, например

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

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

в противном случае вы получите ошибку "Нераспознанный селектор" во время выполнения.

Обновление Swift 2.2+ и Swift 3

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

Selector("keyboardDidHide:")

становится

#selector(keyboardDidHide(_:))

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

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

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

Пример:

@objc func something(_ sender: UIButton)

Swift 4.0

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

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

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

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

@objc func clickedButton(sender: AnyObject) {

}

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

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

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

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

Не изменяя ничего, кроме простого наследования класса от NSObject, я перестал получать сообщение об ошибке "Нераспознанный селектор" и заставил мою логику работать так, как ожидалось.

Если вы хотите передать параметр в функцию из 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, чтобы получить переданный параметр.

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

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 для более подробной информации о: Выражении селектора

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

selector это слово из Objective-C мир, и вы можете использовать его из Swift иметь возможность позвонить Objective-C от Swift Это позволяет вам выполнять некоторый код во время выполнения

Перед Swift 2.2 синтаксис:

Selector("foo:")

Поскольку имя функции передается в Selector как String параметр ("foo") невозможно проверить имя во время компиляции. В результате вы можете получить ошибку времени выполнения:

unrecognized selector sent to instance

После Swift 2.2+ синтаксис:

#selector(foo(_:))

Автозаполнение Xcode поможет вам вызвать правильный метод

Поскольку 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")
        }
    }
}
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()

}

Может быть полезно отметить, где вы устанавливаете элемент управления, запускающий действие.

Например, я обнаружил, что при настройке 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")
    }

Когда используешь 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)

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

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

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

2.

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

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

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

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

Где "Селектор" был объявлен как:

func labelTapped(sender: UILabel) { }

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

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

Поскольку многие заявляют, что селекторы являются объективным способом динамического вызова методов, которые были перенесены в Swift, в некоторых случаях мы все еще застряли с ним, например, с UIKit, вероятно, потому, что они работали над SwiftUI, чтобы заменить его, но некоторые API имеют больше быстрая версия, такая как Swift Timer, например, вы можете использовать

      class func scheduledTimer(withTimeInterval interval: TimeInterval, 
                                            repeats: Bool, 
                                              block: @escaping (Timer) -> Void) -> Timer

Вместо этого вы можете назвать это как

      Timer.scheduledTimer(withTimeInterval: 1, 
                              repeats: true ) {
    ... your test code here
}

или же

      Timer.scheduledTimer(withTimeInterval: 1, 
                              repeats: true,
                              block: test)

где метод test принимает аргумент Timer, или если вы хотите, чтобы test принимал именованный аргумент

      Timer.scheduledTimer(withTimeInterval: 1, 
                              repeats: true,
                              block: test(timer:))

вы также должны использовать Timerне как NSTimerэто старое имя Objective-C

Изменить как простое именование строк в методе, вызывающем синтаксис селектора

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

После этого введите func test().

Селектор в Swift 4:

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

Для 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.

Для быстрой 3

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

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

func test()
{
    // my function
} 
Другие вопросы по тегам