@ selector () в Swift?


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

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

21 593

21 ответ:

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

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

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

помните, что 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   
} 

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

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

The funcдолжны быть публично видимым для вызова объекта со ссылкой на селектор.

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

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

Selector("keyboardDidHide:")

становится

#selector(keyboardDidHide(_:))

Читайте также: Быстрое Эволюционное Предложение

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

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

пример:

@objc func something(_ sender: UIButton)

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

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

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

Swift 4.0

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

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

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

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

@objc func clickedButton(sender: AnyObject) {

}

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

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

}

поскольку Свифт 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")
        }
    }
}

при использовании 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)

создать селектор, как показано ниже.
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 в swift и боролся, поэтому вот что я нашел, работало для меня после прочтения всего выше:

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

где "селектор" был объявлен:

func labelTapped(sender: UILabel) { }

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

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

С помощью #селектора проверит ваш код во время компиляции, чтобы убедиться, что метод, который вы хотите вызвать, действительно существует. Еще лучше, если метод не существует, вы получите ошибку компиляции: 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!")
    }

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

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

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

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

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

для swift 3

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

Объявление Функции В Том Же Классе

func test()
{
    // my function
} 

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