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