Могу ли я сделать #selector ссылкой на закрытие в Swift?
Я хочу, чтобы аргумент selector
моего метода ссылался на свойство закрытия, оба они существуют в одной области. Например,
func backgroundChange() {
self.view.backgroundColor = UIColor.blackColor()
self.view.alpha = 0.55
let backToOriginalBackground = {
self.view.backgroundColor = UIColor.whiteColor()
self.view.alpha = 1.0
}
NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: #selector(backToOriginalBackground), userInfo: nil, repeats: false)
}
Однако это показывает ошибку: Argument of #selector cannot refer to a property
.
Конечно, я могу определить новый, отдельный метод и перенести реализацию закрытия на него, но я хочу, чтобы он был экономным для такой небольшой реализации.
Можно ли установить замыкание на аргумент #selector
?
Ответы
Ответ 1
Как отмечает @gnasher729, это невозможно, потому что селекторы - это просто имена методов, а не сами методы. В общем случае я бы использовал dispatch_after
здесь, но в этом конкретном случае лучший инструмент IMO - UIView.animateWithDuration
, потому что он именно для этой функции, и очень легко настроить переход:
UIView.animateWithDuration(0, delay: 0.5, options: [], animations: {
self.view.backgroundColor = UIColor.whiteColor()
self.view.alpha = 1.0
}, completion: nil)
Ответ 2
Не напрямую, но возможны обходные пути. Взгляните на следующий пример.
/// Target-Action helper.
final class Action: NSObject {
private let _action: () -> ()
init(action: () -> ()) {
_action = action
super.init()
}
func action() {
_action()
}
}
let action1 = Action { print("action1 triggered") }
let button = UIButton()
button.addTarget(action1, action: #selector(action1.action), forControlEvents: .TouchUpInside)
Ответ 3
Нет, #selector ссылается на метод Objective-C.
Вы можете сделать что-то гораздо лучше: добавьте расширение в NSTimer, которое позволяет создавать запланированный таймер не с целью и селектором, а с закрытием.
Ответ 4
Теперь это возможно. Я создал сущность для блочных селекторов в Swift 4.
https://gist.github.com/cprovatas/98ff940140c8744c4d1f3bcce7ba4543
Использование:
UIButton().addTarget(Selector, action: Selector { debugPrint("my code here") }, for: .touchUpInside)
Ответ 5
Если вы измените область действия блока на область класса, а не на функцию, и сохраните ссылку на ее закрытие.
Вы можете вызвать это закрытие с помощью функции. в классе. Таким образом, вы можете вызвать это закрытие как селектор.
Что-то вроде этого:
class Test: NSObject {
let backToOriginalBackground = {
}
func backgroundChange() {
NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: #selector(test), userInfo: nil, repeats: false)
}
func test() {
self.backToOriginalBackground()
}
}
Ответ 6
Мое решение состояло в создании переменной блока класса, например:
let completionBlock: () -> () = nil
Создайте метод, который вызывает это завершениеBlock:
func completed(){
self.completionBlock!()
}
И внутри, где я хочу поставить свой селектор как блок, я сделал:
func myFunc(){
self.completionBlock = {//what I want to be done}
NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: #selector(Myclass.completed), userInfo: nil, repeats: false)
}
Ответ 7
Итак, мой ответ на то, чтобы selector
был назначен closure
быстрым способом, похож на некоторые из ответов уже, но я думал, что поделюсь примером реальной жизни, как я это сделал в UIViewController
.
fileprivate class BarButtonItem: UIBarButtonItem {
var actionCallback: ( () -> Void )?
func buttonAction() {
actionCallback?()
}
}
fileprivate extension Selector {
static let onBarButtonAction = #selector(BarButtonItem.buttonAction)
}
extension UIViewController {
func createBarButtonItem(title: String, action: @escaping () -> Void ) -> UIBarButtonItem {
let button = BarButtonItem(title: title, style: .plain, target nil, action: nil)
button.actionCallback = action
button.action = .onBarButtonAction
return button
}
}
// Example where button is inside a method of a UIViewController
// and added to the navigationItem of the UINavigationController
let button = createBarButtonItem(title: "Done"){
print("Do something when done")
}
navigationItem.setLeftbarButtonItems([button], animated: false)
Ответ 8
Я попробовал это для UIBarButtonItem:
private var actionKey: Void?
extension UIBarButtonItem {
private var _action: () -> () {
get {
return objc_getAssociatedObject(self, &actionKey) as! () -> ()
}
set {
objc_setAssociatedObject(self, &actionKey, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
convenience init(title: String?, style: UIBarButtonItemStyle, action: @escaping () -> ()) {
self.init(title: title, style: style, target: nil, action: #selector(pressed))
self.target = self
self._action = action
}
@objc private func pressed(sender: UIBarButtonItem) {
_action()
}
}
Затем вы можете сделать это:
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Test", style: .plain, action: {
print("Hello World!")
})